大規模客戶端應用一般很差實現很差組織也很差維護,由於功能和人力的不斷增長,這些應用的規模很快就會超出掌控能力,ExtJS4帶來了一個新的應用架構,不但能夠組織代碼,還能夠減小實現的內容。css
新的應用架構遵守一個類MVC的模式,模型(Models)和控制器(Controllers)首次被引入。業界有不少種MVC架構,基本大同小異,ExtJS4的定義以下:html
a.Model模型:模型是字段和它們的數據的集合,例如User模型帶有username和password字段,模型知道如何持久化本身的數據,而且能夠和其餘模型關聯,模型跟ExtJS 3 中的Record類有點像(區別是,Record只是單純的扁平結構,而Model能夠nest),一般都用在Store中去展現grid和其餘組件的數據。ajax
b.View視圖:視圖是組件的一種,專一於界面展現 – grid, tree, panel 都是view。數據庫
c.Controllers控制器:一個安放全部使你的app正確工做的代碼的位置,具體一點應該是全部動做,例如如何渲染view,如何初始化model,和app的其餘邏輯。json
請注意:MVC是一個框架,不是設計模式,更多的內容請參考: 百度百科設計模式
框架與設計模式雖然類似,但卻有着根本的不一樣。設計模式是對在某種環境中反覆出現的問題以及解決該問題的方案的描述,它比框架更抽象;框架能夠用代碼表示,也能直接執行或複用,而對模式而言只有實例才能用代碼表示;設計模式是比框架更小的元素,一個框架中每每含有一個或多個設計模式,框架老是針對某一特定應用領域,但同一模式卻可適用於各類應用。能夠說,框架是軟件,而設計模式是軟件的知識。api
簡而言之:設計模式是大智慧,用來對軟件設計進行分工;框架模式是小技巧,對具體問題提出解決方案,以提升代碼複用率,下降耦合度。數組
ExtJS 4 應用都遵循一個統一的目錄結構,每一個應有都相同 MVC中,全部類都放在app
目錄裏面,這個目錄能夠有子目錄,表明的是命名空間(一個子目錄對應一個命名空間),使用不一樣的目錄存放views
,models
,controllers
,stores
。當咱們完成例子的時候,目錄結構應該和下圖同樣:服務器
ExtJS SDK必須的文件在目錄ext4中,所以,index.html
應該引入extjs必須的js和css,以及app.js文件 架構
每一個ExtJS 4的應用都從一個Application
類的實例開始,這個實例包含應用的全局配置(例如應用的名字),這個實例也負責維護對所有模型、視圖、控制器的引用的維護,還有一個launch
函數,會在全部加載項加載完成以後調用。
首先須要選擇一個全局命名空間,全部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 : '內容' } ] }); } });
控制器是應用的粘合劑,它們所做的事情就是監聽事件並執行動做,繼續咱們的應用,建立一個控制器。建立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查看應用,Students控制器會被自動加載(由於在app.js的Application中增長了引用),而且Students的init方法會在launch以前調用。
init
方法是個極好的地方,能夠用來設置如何和view交互,一般都使用Controller的一個方法control,control方法使得監聽view的事件變的容易,更新一下控制器,讓它告知咱們panel什麼時候渲染:
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給視圖設置監聽器。這個controll方法,使用最新的組件查詢引擎(ComponentQuery)能夠快速方便的找到頁面上的組件。若是你對ComponentQuery不熟悉,能夠查看ComponentQuery文檔進行詳細瞭解。簡要一點,ComponentQuery能夠容許咱們使用一個相似css選擇器的方式找到組件。
在例子的init方法中咱們應用了'viewport > panel',能夠解釋爲「查找Viewport直接後代中的全部Panel組件」,而後咱們提供了一個對象匹配事件名稱(這個例子中只用了render)來提供響應函數。所有的影響就是不管哪一個組件符合咱們的選擇器,當它的render事件觸發時,咱們的onPanelRendered函數都會被調用。
直到如今,咱們的應用只有不多代碼,只有兩個文 件 app.js 和 app/controller/Students.js,如今咱們想增長一個grid顯示全部系統中的學生列表,修改3處:
是時候更好的組織一下邏輯並開始使用視圖了。
視圖也是組件,一般都是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就是規則,把.替換成/就是文件存放路徑。刷新一下頁面便可看到效果
分三步完成對對編輯窗體的控制
注意 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屬性。
能夠看到日誌是正確的,但咱們實際想作的是編輯用戶信息,讓咱們如今作,建立一個新的視圖 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
方法,能夠藉助組件查詢支持的選擇器來迅速找到任意下層的組件,雙擊表格中的一行能夠看到彈窗效果。
如今咱們有了表單,能夠開始編輯和保存用戶信息了,可是這以前須要作一點點重構。 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:
//修改控制器,引用Model Ext.define('FWY.controller.Students', { extend: 'Ext.app.Controller', stores: ['Students'], models: ['Student'], ... }); //修改store,引用Model Ext.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中 }
最後附上本篇的代碼:點擊下載
--本篇完--