一,、ExtJs 4.x MVC模式的原理與作用大規(guī)??蛻舳藨?yīng)用通常不好實(shí)現(xiàn)不好組織也不好維護(hù),因?yàn)楣δ芎腿肆Φ牟粩嘣黾?,這些應(yīng)用的規(guī)模很快就會(huì)超出掌控能力,,ExtJS4帶來(lái)了一個(gè)新的應(yīng)用架構(gòu),不但可以組織代碼,,還可以減少實(shí)現(xiàn)的內(nèi)容,。 新的應(yīng)用架構(gòu)遵照一個(gè)類MVC的模式,模型(Models)和控制器(Controllers)首次被引入,。業(yè)界有很多種MVC架構(gòu),,基本大同小異,ExtJS4的定義如下: a.Model模型:模型是字段和它們的數(shù)據(jù)的集合,,例如User模型帶有username和password字段,,模型知道如何持久化自己的數(shù)據(jù),并且可以和其他模型關(guān)聯(lián),模型跟ExtJS 3 中的Record類有點(diǎn)像(區(qū)別是,,Record只是單純的扁平結(jié)構(gòu),,而Model可以nest),通常都用在Store中去展示grid和其他組件的數(shù)據(jù),。 b.View視圖:視圖是組件的一種,,專注于界面展示 – grid, tree, panel 都是view。 c.Controllers控制器:一個(gè)安放所有使你的app正確工作的代碼的位置,,具體一點(diǎn)應(yīng)該是所有動(dòng)作,,例如如何渲染view,如何初始化model,,和app的其他邏輯,。 請(qǐng)注意:MVC是一個(gè)框架,不是設(shè)計(jì)模式,,更多的內(nèi)容請(qǐng)參考: 百度百科 框架與設(shè)計(jì)模式雖然相似,,但卻有著根本的不同。設(shè)計(jì)模式是對(duì)在某種環(huán)境中反復(fù)出現(xiàn)的問(wèn)題以及解決該問(wèn)題的方案的描述,,它比框架更抽象,;框架可以用代碼表示,也能直接執(zhí)行或復(fù)用,,而對(duì)模式而言只有實(shí)例才能用代碼表示;設(shè)計(jì)模式是比框架更小的元素,,一個(gè)框架中往往含有一個(gè)或多個(gè)設(shè)計(jì)模式,框架總是針對(duì)某一特定應(yīng)用領(lǐng)域,,但同一模式卻可適用于各種應(yīng)用,。可以說(shuō),,框架是軟件,,而設(shè)計(jì)模式是軟件的知識(shí)。 簡(jiǎn)而言之:設(shè)計(jì)模式是大智慧,,用來(lái)對(duì)軟件設(shè)計(jì)進(jìn)行分工,;框架模式是小技巧,對(duì)具體問(wèn)題提出解決方案,,以提高代碼復(fù)用率,,降低耦合度。 二,、ExtJs 4 MVC框架搭建2.1,、文件結(jié)構(gòu)ExtJS 4 應(yīng)用都遵循一個(gè)統(tǒng)一的目錄結(jié)構(gòu),每個(gè)應(yīng)有都相同 MVC中,,所有類都放在 ExtJS SDK必須的文件在目錄ext4中,因此,, 2.2、在app.js中創(chuàng)建應(yīng)用每個(gè)ExtJS 4的應(yīng)用都從一個(gè) 首先需要選擇一個(gè)全局命名空間,,所有ExtJS4應(yīng)用都需要有一個(gè)全局命名空間,以讓所有應(yīng)用中的類安放到其中:
Ext.application({
requires: ['Ext.container.Viewport'],
name: 'FWY',//定義的命名空間
appFolder: 'app',//指明應(yīng)用的根目錄
launch: function() {
Ext.create('Ext.container.Viewport', {
layout: 'fit',
items: [
{
xtype: 'panel',
title: '標(biāo)題',
html : '內(nèi)容'
}
]
});
}
});
2.3,、定義一個(gè)控制器控制器是應(yīng)用的粘合劑,,它們所作的事情就是監(jiān)聽(tīng)事件并執(zhí)行動(dòng)作,繼續(xù)我們的應(yīng)用,,創(chuàng)建一個(gè)控制器。創(chuàng)建
Ext.define('FWY.controller.Students', {
extend: 'Ext.app.Controller',
init: function() {
console.debug('trigger controller init event');
}
});
接下來(lái)在
Ext.application({
...
controllers: [
'Students' //對(duì)應(yīng)于controller文件夾下面的Students.js
],
...
});
當(dāng)我們通過(guò)index.html查看應(yīng)用,,Students控制器會(huì)被自動(dòng)加載(因?yàn)樵赼pp.js的Application中增加了引用),并且Students的init方法會(huì)在launch之前調(diào)用,。
Ext.define('FWY.controller.Students', {
extend: 'Ext.app.Controller',
init: function() {
this.control({
'viewport > panel': {
render: this.onPanelRendered
}
});
},
onPanelRendered: function() {
console.debug('該panel被渲染了');
}
});
我們已經(jīng)更新了init方法,,使用this.controll給視圖設(shè)置監(jiān)聽(tīng)器,。這個(gè)controll方法,使用最新的組件查詢引擎(ComponentQuery)可以快速方便的找到頁(yè)面上的組件,。如果你對(duì)ComponentQuery不熟悉,,可以查看ComponentQuery文檔進(jìn)行詳細(xì)了解。簡(jiǎn)要一點(diǎn),,ComponentQuery可以允許我們使用一個(gè)類似css選擇器的方式找到組件,。 在例子的init方法中我們應(yīng)用了'viewport > panel',,可以解釋為“查找Viewport直接后代中的所有Panel組件”,然后我們提供了一個(gè)對(duì)象匹配事件名稱(這個(gè)例子中只用了render)來(lái)提供響應(yīng)函數(shù),。全部的影響就是無(wú)論哪個(gè)組件符合我們的選擇器,,當(dāng)它的render事件觸發(fā)時(shí),我們的onPanelRendered函數(shù)都會(huì)被調(diào)用,。 三,、創(chuàng)建ExtJs4 MVC應(yīng)用1、定義一個(gè)視圖直到現(xiàn)在,,我們的應(yīng)用只有很少代碼,,只有兩個(gè)文 件 app.js 和 app/controller/Students.js,現(xiàn)在我們想增加一個(gè)grid顯示所有系統(tǒng)中的學(xué)生列表,,修改3處: (1),、添加view/List.js視圖是時(shí)候更好的組織一下邏輯并開(kāi)始使用視圖了。 視圖也是組件,,通常都是ExtJS現(xiàn)有組件的子類,,現(xiàn)在準(zhǔn)備創(chuàng)建學(xué)生表,先創(chuàng)建
Ext.define('FWY.view.student.List' ,{
extend: 'Ext.grid.Panel',
alias : 'widget.studentlist',
title : '學(xué)生信息列表',
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: '編號(hào)', dataIndex: 'id', flex: 1},
{header: '姓名', dataIndex: 'name', flex: 1},
{header: '年齡', dataIndex: 'age', flex: 1},
{header: '性別', dataIndex: 'sex', flex: 1}
];
this.callParent(arguments);
}
});
(2),、修改controller/Students.js我們的視圖類就是一個(gè)普通的類,這個(gè)例子中我們擴(kuò)展了 Grid 組件,,并設(shè)置了別名,,這樣我們可以用 xtype 的方式調(diào)用這個(gè)組件,另外我們也添加了 store 和 columns 的配置,。 接下來(lái)我們需要添加這個(gè)視圖到 Students控制器,。因?yàn)槲覀冇?'widget.studentlist' 設(shè)置了別名,所以我們可以使用 studentlist 作為xtype,,就像我們使用之前使用的 'panel'
Ext.define('FWY.controller.Students', {
extend: 'Ext.app.Controller',
views: [
'student.List'//添加view視圖
],
init: ...
onPanelRendered: ...
});
(3),、修改app.js,加載視圖接下來(lái)修改 app.js 讓視圖在viewport中渲染,,需要修改 launch 方法
Ext.application({
...
launch: function() {
Ext.create('Ext.container.Viewport', {
layout: 'fit',
items: {
xtype: 'studentlist'
}
});
}
});
唯一需要注意的是我們?cè)趘iews數(shù)組中指定了 'student.List' ,,這告訴應(yīng)用去自動(dòng)加載對(duì)應(yīng)的文件,ExtJS4 的動(dòng)態(tài)加載系統(tǒng)會(huì)根據(jù)規(guī)則從服務(wù)器自動(dòng)拉取文件,,例如student.List就是規(guī)則,,把.替換成/就是文件存放路徑。刷新一下頁(yè)面即可看到效果 2,、添加對(duì)列表的控制分三步完成對(duì)對(duì)編輯窗體的控制 (1),、為grid綁定雙擊事件注意 onPanelRendered 方法依然被調(diào)用,因?yàn)槲覀兊膅rid依然滿足 'viewport > panel' 選擇器,,因?yàn)槲覀兊囊晥D繼承自 Grid ,,從而繼承自 Panel?,F(xiàn)在我們需要收緊一下選擇器,我們使用xtype作為選擇器替換之前的 'viewport > panel' ,,監(jiān)聽(tīng)雙擊事件,,以便繼續(xù)做編輯用戶信息:
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' ,監(jiān)聽(tīng)的事件更改為 'itemdblclick' ,,響應(yīng)函數(shù)設(shè)置為 editStudent,,現(xiàn)在只是簡(jiǎn)單的日志出雙擊行的name屬性。 (2),、創(chuàng)建view/student/Edit.js視圖可以看到日志是正確的,,但我們實(shí)際想做的是編輯用戶信息,讓我們現(xiàn)在做,,創(chuàng)建一個(gè)新的視圖
Ext.define('FWY.view.student.Edit', {
extend: 'Ext.window.Window',
alias : 'widget.studentedit',
title : '修改學(xué)生信息',
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);
}
});
(3),、修改控制器加載視圖接下來(lái)我們要做的就是在控制器加載這個(gè)視圖,渲染并且加載用戶信息:
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');//注冊(cè)組件,,顯示窗口
view.down('form').loadRecord(record);//加載數(shù)據(jù)到表單中
}
});
首先我們用 Ext.widget 方法創(chuàng)建了視圖,,這個(gè)方法等同于 3,、創(chuàng)建Store和Model進(jìn)行重構(gòu)現(xiàn)在我們有了表單,,可以開(kāi)始編輯和保存用戶信息了,但是這之前需要做一點(diǎn)點(diǎn)重構(gòu),。 FWY.view.student.List 創(chuàng)建了一個(gè)內(nèi)聯(lián)的 Store ,,這樣可以工作但是我們需要把 Store 分離出來(lái)以便我們?cè)趹?yīng)用的其他位置可以引用并更新其中的信息,我們把它放在它應(yīng)該在的文件中
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:'女'}
]
});
現(xiàn)在我們需要做兩處變更,,首先我們需要讓 Students 初始化的時(shí)候加載這個(gè) Store :
Ext.define('FWY.controller.Students', {
extend: 'Ext.app.Controller',
stores: ['Students'],//加載store
...
});
然后我們要把之前直接在視圖中內(nèi)聯(lián)的store更改掉,,
Ext.define('FWY.view.student.List' ,{
extend: 'Ext.grid.Panel',
alias : 'widget.studentlist',
store: 'Students',//引用Store
...
});
控制器的代碼中中引入了store,,store會(huì)被自動(dòng)加載到頁(yè)面并賦予一個(gè)storeId,,這讓視圖中使用store變的容易(這個(gè)例子中,只要配置 store: 'Students' 就可以了) 現(xiàn)在我們只是在store中內(nèi)聯(lián)的定義了四個(gè)字段 (id,name,age,sex),,這樣可以工作了,。 進(jìn)一步重構(gòu): ExtJS4中有一個(gè)強(qiáng)大的 Ext.data.Model類,在編輯用戶的時(shí)候我們可以借助它,,使用Model重構(gòu)下Store,,在
Ext.define('FWY.model.Student', {
extend: 'Ext.data.Model',
fields: ['id','name','age','sex']
});
這就是定義我們的Model需要做的,現(xiàn)在需要讓Store引用Model替換掉使用內(nèi)聯(lián)字段的方式,,并且讓控制器也引用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:'女'}
]
});
4、利用模型保存數(shù)據(jù)現(xiàn)在我們有了一個(gè)用戶數(shù)據(jù)表,,雙擊每?一行都能打開(kāi)一個(gè)編輯窗口,,現(xiàn)在要做的是保存編輯變更,編輯窗口有一個(gè)編輯表單,,還有保存按鈕,,現(xiàn)在我們更新一下控制器讓保存按鈕有響應(yīng):
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');
}
});
接下來(lái)填充 updateStudent 真正的邏輯。我們需要把數(shù)據(jù)從表單中取出,,再 設(shè)置回store中:
updateStudent: function(button) {
var win = button.up('window'),
form = win.down('form'),
record = form.getRecord(),
values = form.getValues();
record.set(values);
win.close();
}
5,、保存到服務(wù)器讓我們?cè)黾雍头?wù)器端的交互完成這個(gè)例子。現(xiàn)在我們還是應(yīng)編碼了兩行表格的數(shù) 據(jù),,現(xiàn)在讓我們通過(guò)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'
}
}
});
這里我們?nèi)コ?'data' 屬性,,替換成 proxy ,代理是讓Store或者M(jìn)odel加載和保存數(shù)據(jù)的一個(gè)方式,,有AJAX,,JSONP,HTML5的localStorage本地存儲(chǔ)等,。這里我們使用了一個(gè)簡(jiǎn)單的AJAX代理,,讓它通過(guò)URL 'data/students.json' 加載數(shù)據(jù)。 我們同時(shí)給代理附加了一個(gè)reader,,reader是用來(lái)把服務(wù)器返回的數(shù)據(jù)解碼成Store能理解的格式,,這次我們使用了JSON reader,并且指定了root和 successProperty 配置(JSON reader的詳細(xì)配置看文檔),,最后我們創(chuàng)建一下數(shù)據(jù)文件
{
success: true,
users: [
{id: 1, name: 'zhang', email: '[email protected]'},
{id: 2, name: 'lishi', email: '[email protected]'}
]
}
其他的變更就是我們給Store設(shè)置了 autoLoad 屬性并設(shè)置為 true ,這意味著Store生成之后會(huì)自動(dòng)讓Proxy加載數(shù)據(jù),,刷新?一下頁(yè)面應(yīng)該是看到和之前同樣的結(jié)果,,不同的是現(xiàn)在不是在程序中存在硬編碼數(shù)據(jù)了,最后的事情是將變更傳回服務(wù)器端,,這個(gè)例子中我們使用靜態(tài)的JSON文件,,沒(méi)有使用數(shù)據(jù)庫(kù),但足夠說(shuō)明我們例子的了,,首先做一點(diǎn)點(diǎn)變化告知proxy用于更新的url:
proxy: {
type: 'ajax',
api: {
read: 'data/students.json',
update: 'data/updateStudents.json',
},
reader: {
type: 'json',
root: 'students',
successProperty: 'success'
}
}
依然從 students.json 讀取數(shù)據(jù),,但是變更會(huì)發(fā)送到 updateStudents.json ,這里我們做?個(gè)模擬的應(yīng)答回包以讓我們知道程序可以正確工作,, updateStudents.json 只需要包含
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();//將數(shù)據(jù)同步到store中
}
最后附上本篇的代碼:點(diǎn)擊下載 --本篇完-- |
|