1. ExtJs 4.x MVC モデルの原理と機能
通常、大規模なクライアント アプリケーションは、機能と人員が継続的に増加するため、制御不能になる可能性があります。 ExtJS4 は、コードを整理するだけでなく、実装内容を削減できる新しいアプリケーション アーキテクチャをもたらします。
新しいアプリケーション アーキテクチャは MVC のようなパターンに従い、モデルとコントローラーが初めて導入されました。業界には多くの MVC アーキテクチャがあり、基本的には同じです。 ExtJS4 の定義は次のとおりです。
a.Model モデル: モデルはフィールドとそのデータのコレクションです。たとえば、User モデルにはユーザー名とそのデータがあります。モデルは独自のデータを保持する方法を知っており、他のモデルと関連付けることができます。このモデルは ExtJS 3 の Record クラスに似ています (違いは、Record が単なるフラットな構造であることです)。ネストされています)。これは通常、グリッドやその他のコンポーネントからのデータを表示するためにストアで使用されます。
b.View: ビューはコンポーネントの一種で、インターフェイスの表示に重点を置いています。グリッド、ツリー、パネルはすべてビューです。
c.Controllers: アプリを正しく動作させるすべてのコードを配置する場所。具体的には、ビューのレンダリング方法、モデルの初期化方法、アプリのその他のロジックなどのすべてのアクションです。
注意: MVC はフレームワークであり、デザイン パターンではありません。詳細については、Baidu 百科事典を参照してください。
フレームワークとデザイン パターンは似ていますが、根本的に異なります。デザイン パターンは、特定の環境で繰り返される問題とその解決策を記述したものであり、フレームワークはコードで表現でき、直接実行または再利用できますが、コードで表現できるのはインスタンスだけです。 ; デザイン パターンはフレームワークよりも小さな要素です。フレームワークには 1 つ以上のデザイン パターンが含まれることがよくありますが、同じパターンをさまざまなアプリケーションに適用できます。フレームワークはソフトウェアであり、デザインパターンはソフトウェアの知識であると言えます。つまり、デザイン パターンは優れた知恵であり、ソフトウェア デザインを分割するために使用されます。コードの再利用を改善し、結合を減らすために特定の問題に対する解決策を提案する小さなスキルです。
2. ExtJs 4 MVC フレームワークの構築 2.1. ファイル構造
ExtJS 4 アプリケーションは、統一されたディレクトリ構造に従う必要があります。このディレクトリは、名前空間を表すサブディレクトリを持つことができます。 (サブディレクトリは名前空間に対応します)、ビュー、モデル、コントローラー、ストアを保存するために異なるディレクトリを使用します。この例を完了すると、ディレクトリ構造は次のようになります:
ExtJS SDK に必要なファイルはディレクトリ ext4 にあります。したがって、index.html には extjs に必要な js と css が導入されるはずです。 app.js ファイル
2.2 、app.js でアプリケーションを作成する
すべての ExtJS 4 アプリケーションは、Application クラスのインスタンスから始まります。このインスタンスには、アプリケーションのグローバル構成 (アプリケーションの名前など) が含まれています。すべてのモデル、ビュー、コントロールの維持も担当します。すべてのアドインが読み込まれた後に呼び出される起動関数もあります。まず、グローバル名前空間を選択する必要があります。アプリケーション内のすべてのクラスをその中に配置できるように、すべての ExtJS4 アプリケーションにはグローバル名前空間が必要です。
Ext.application({ requires: ['Ext.container.Viewport'], name: 'FWY',//定义的命名空间 appFolder: 'app',//指明应用的根目录 launch: function() { Ext.create('Ext.container.Viewport', { layout: 'fit', items: [ { xtype: 'panel', title: '标题', html : '内容' } ] }); }});
2.3. コントローラーを定義します。アプリケーション エージェントは、イベントをリッスンしてアクションを実行し、アプリケーションを続行し、コントローラーを作成します。ファイル app/controller/Students.js を作成し、次のコードを追加します:
Ext.define('FWY.controller.Students', { extend: 'Ext.app.Controller', init: function() { console.debug("trigger controller init event"); }});
次に、app.js に Students コントローラーへの参照を追加します:
Ext.application({...controllers: [ 'Students' //对应于controller文件夹下面的Students.js],...});
Index.html を通じてアプリケーションを表示すると、 Student コントローラーは自動的にロードされ (app.js のアプリケーションに参照が追加されるため)、起動前に Student の init メソッドが呼び出されます。
init メソッドは、ビューと対話する方法を設定するのに最適な場所です。通常、このコントロール メソッドを使用すると、ビューのイベントを簡単に監視して通知できます。パネルがどこにあるかを確認します。 レンダリング時間:
Ext.define('FWY.controller.Students', { extend: 'Ext.app.Controller', init: function() { this.control({ 'viewport > panel': { render: this.onPanelRendered } }); }, onPanelRendered: function() { console.debug('该panel被渲染了'); }});
init メソッドを更新し、this.controll を使用してビューのリスナーを設定しました。この制御方法では、最新のコンポーネント クエリ エンジン (ComponentQuery) を使用して、ページ上のコンポーネントを迅速かつ簡単に検索します。 ComponentQuery に詳しくない場合は、ComponentQuery のドキュメントを参照して詳細を確認してください。簡単に言うと、ComponentQuery を使用すると、CSS セレクターのような方法を使用してコンポーネントを検索できます。
この例の init メソッドでは、「ビューポート > パネル」を適用します。これは「ビューポートの直接の子孫にあるすべてのパネル コンポーネントを検索する」と解釈でき、イベント名に一致するオブジェクトを提供します (レンダリングのみこの例では ) を使用して応答関数を提供します。全体的な影響は、どのコンポーネントがセレクターに一致するかに関係なく、レンダリング イベントが発生すると、onPanelRendered 関数が呼び出されるということです。
三、创建ExtJs4 MVC应用 1、定义一个视图直到现在,我们的应用只有很少代码,只有两个文 件 app.js 和 app/controller/Students.js,现在我们想增加一个grid显示所有系统中的学生列表,修改3处:
(1)、添加view/List.js视图是时候更好的组织一下逻辑并开始使用视图了。
视图也是组件,通常都是ExtJS现有组件的子类,现在准备创建学生表,先创建 app/view/student/List.js ,添加代码:
Ext.define('FWY.view.student.List' ,{ extend: 'Ext.grid.Panel', alias : 'widget.studentlist', title : '学生信息列表', initComponent: function() { this.store = { fields: ['id','name', 'age','sex'], data : [ {id:1,name: 'zhangsan', age: 18,sex:'boy'}, {id:2,name: 'lishi', age: 20,sex:'girl'} ]}; this.columns = [ {header: '编号', dataIndex: 'id', flex: 1}, {header: '姓名', dataIndex: 'name', flex: 1}, {header: '年龄', dataIndex: 'age', flex: 1}, {header: '性别', dataIndex: 'sex', flex: 1} ]; this.callParent(arguments); }});
我们的视图类就是一个普通的类,这个例子中我们扩展了 Grid 组件,并设置了别名,这样我们可以用 xtype 的方式调用这个组件,另外我们也添加了 store 和 columns 的配置。 接下来我们需要添加这个视图到 Students控制器。因为我们用 'widget.studentlist' 设置了别名,所以我们可以使用 studentlist 作为xtype,就像我们使用之前使用的 'panel'
Ext.define('FWY.controller.Students', { extend: 'Ext.app.Controller', views: [ 'student.List'//添加view视图 ], init: ... onPanelRendered: ...});
接下来修改 app.js 让视图在viewport中渲染,需要修改 launch 方法
Ext.application({ ... launch: function() { Ext.create('Ext.container.Viewport', { layout: 'fit', items: { xtype: 'studentlist' } }); }});
唯一需要注意的是我们在views数组中指定了 'student.List' ,这告诉应用去自动加载对应的文件,ExtJS4 的动态加载系统会根据规则从服务器自动拉取文件,例如student.List就是规则,把.替换成/就是文件存放路径。刷新一下页面即可看到效果
2、添加对列表的控制分三步完成对对编辑窗体的控制
(1)、为grid绑定双击事件注意 onPanelRendered 方法依然被调用,因为我们的grid依然满足 'viewport > panel' 选择器,因为我们的视图继承自 Grid ,从而继承自 Panel。现在我们需要收紧一下选择器,我们使用xtype作为选择器替换之前的 'viewport > panel' ,监听双击事件,以便继续做编辑用户信息:
Ext.define('FWY.controller.Students', { extend: 'Ext.app.Controller', views: [ 'student.List' ], init: function() { this.control({ 'studentlist': { itemdblclick: this. editStudent//添加行双击事件 } }); }, editStudent: function(grid, record) { console.log('Double clicked on ' + record.get('name')); }});
注意我们更换了组件查询选择器为 'studentlist' ,监听的事件更改为 'itemdblclick' ,响应函数设置为 editStudent,现在只是简单的日志出双击行的name属性。
(2)、创建view/student/Edit.js视图可以看到日志是正确的,但我们实际想做的是编辑用户信息,让我们现在做,创建一个新的视图 app/view/student/Edit.js
Ext.define('FWY.view.student.Edit', { extend: 'Ext.window.Window', alias : 'widget.studentedit', title : '修改学生信息', layout: 'fit', autoShow: true, initComponent: function() { this.items = [ { xtype: 'form', items: [ { xtype: 'textfield', name : 'name', fieldLabel: '姓名' }, { xtype: 'textfield', name : 'age', fieldLabel: '年龄' }, { xtype: 'textfield', name : 'sex', fieldLabel: '性别' } ] } ]; this.buttons = [ { text: '保存', action: 'save' }, { text: '取消', scope: this, handler: this.close } ]; this.callParent(arguments); }});
接下来我们要做的就是在控制器加载这个视图,渲染并且加载用户信息:
Ext.define('FWY.controller.Students', { extend: 'Ext.app.Controller', views: [ 'student.List', 'student.Edit'//添加edit视图 ], init: ... editStudent: function(grid, record) { var view = Ext.widget('studentedit');//注册组件,显示窗口 view.down('form').loadRecord(record);//加载数据到表单中 }});
首先我们用 Ext.widget 方法创建了视图,这个方法等同于 Ext.create('widget.studentedit') ,然后我们又一次借助组件查询找到了窗口中的表单,每个ExtJS4中的组件都有一个 down方法,可以借助组件查询支持的选择器来迅速找到任意下层的组件,双击表格中的一行可以看到弹窗效果。
3、创建Store和Model进行重构现在我们有了表单,可以开始编辑和保存用户信息了,但是这之前需要做一点点重构。 FWY.view.student.List 创建了一个内联的 Store ,这样可以工作但是我们需要把 Store 分离出来以便我们在应用的其他位置可以引用并更新其中的信息,我们把它放在它应该在的文件中 app/store/Students.js :
Ext.define('FWY.store.Students', { extend: 'Ext.data.Store', fields: ['id','name', 'age','sex'], data: [ {id:1,name: '张三', age: 30,sex:'男'}, {id:2,name: '李四', age: 20,sex:'女'} ]});
现在我们需要做两处变更,首先我们需要让 Students 初始化的时候加载这个 Store :
Ext.define('FWY.controller.Students', { extend: 'Ext.app.Controller', stores: ['Students'],//加载store ...});
然后我们要把之前直接在视图中内联的store更改掉,
Ext.define('FWY.view.student.List' ,{ extend: 'Ext.grid.Panel', alias : 'widget.studentlist', store: 'Students',//引用Store ...});
控制器的代码中中引入了store,store会被自动加载到页面并赋予一个storeId,这让视图中使用store变的容易(这个例子中,只要配置 store: 'Students' 就可以了) 现在我们只是在store中内联的定义了四个字段 (id,name,age,sex),这样可以工作了。
进一步重构:
ExtJS4中有一个强大的 Ext.data.Model类,在编辑用户的时候我们可以借助它,使用Model重构下Store,在 app/model/Student.js中创建一个Model:
Ext.define('FWY.model.Student', { extend: 'Ext.data.Model', fields: ['id','name','age','sex']});
这就是定义我们的Model需要做的,现在需要让Store引用Model替换掉使用内联字段的方式,并且让控制器也引用Model:
//修改控制器,引用ModelExt.define('FWY.controller.Students', { extend: 'Ext.app.Controller', stores: ['Students'], models: ['Student'], ...});//修改store,引用ModelExt.define('FWY.store.Students', { extend: 'Ext.data.Store', model: 'FWY.model.Student', data: [ {id:1,name: '张三1', age: 30,sex:'男'}, {id:2,name: '李四1', age: 21,sex:'女'} ]});
现在我们有了一个用户数据表,双击每?一行都能打开一个编辑窗口,现在要做的是保存编辑变更,编辑窗口有一个编辑表单,还有保存按钮,现在我们更新一下控制器让保存按钮有响应:
Ext.define('FWY.controller.Students', { init: function() { this.control({ 'viewport > studentlist': { itemdblclick: this.editStudent }, 'studentedit button[action=save]': {//获取studentedit视图中的button配置action=‘save’的按钮事件 click: this.updateStudent } }); }, updateStudent: function(button) { console.log('clicked the Save button'); }});
接下来填充 updateStudent 真正的逻辑。我们需要把数据从表单中取出,再 设置回store中:
updateStudent: function(button) { var win = button.up('window'), form = win.down('form'), record = form.getRecord(), values = form.getValues(); record.set(values); win.close();}
让我们增加和服务器端的交互完成这个例子。现在我们还是应编码了两行表格的数 据,现在让我们通过ajax加载:
Ext.define('FWY.store.Students', { extend: 'Ext.data.Store', model: 'FWY.model.Student', autoLoad: true, proxy: { type: 'ajax', url: 'data/students.json', reader: { type: 'json', root: 'students', successProperty: 'success' } }});
这里我们去除了 'data' 属性,替换成 proxy ,代理是让Store或者Model加载和保存数据的一个方式,有AJAX,JSONP,HTML5的localStorage本地存储等。这里我们使用了一个简单的AJAX代理,让它通过URL 'data/students.json' 加载数据。
我们同时给代理附加了一个reader,reader是用来把服务器返回的数据解码成Store能理解的格式,这次我们使用了JSON reader,并且指定了root和 successProperty 配置(JSON reader的详细配置看文档),最后我们创建一下数据文件 data/students.json ,输入内容:
{ success: true, users: [ {id: 1, name: 'zhang', email: 'zhang@126.com'}, {id: 2, name: 'lishi', email: 'lishi@126.com'} ]}
其他的变更就是我们给Store设置了 autoLoad 属性并设置为 true ,这意味着Store生成之后会自动让Proxy加载数据,刷新?一下页面应该是看到和之前同样的结果,不同的是现在不是在程序中存在硬编码数据了,最后的事情是将变更传回服务器端,这个例子中我们使用静态的JSON文件,没有使用数据库,但足够说明我们例子的了,首先做一点点变化告知proxy用于更新的url:
proxy: { type: 'ajax', api: { read: 'data/students.json', update: 'data/updateStudents.json', }, reader: { type: 'json', root: 'students', successProperty: 'success' }}
依然从 students.json 读取数据,但是变更会发送到 updateStudents.json ,这里我们做?个模拟的应答回包以让我们知道程序可以正确工作, updateStudents.json 只需要包含{"success":true},其他要做的就是让Store在编辑之后进行同步,需要在 updateStudent 函数中增加一行代码:
updateStudent: function(button) { var win = button.up('window'), form = win.down('form'), record = form.getRecord(), values = form.getValues(); record.set(values); win.close(); this.getStudentsStore().sync();//将数据同步到store中}
最后附上本篇的代码:点击下载
--本篇完--