數(shù)據(jù)包是你應(yīng)用程序加載和保存所有數(shù)據(jù)的地方踱讨。它包含大量的類檐涝,其中有三個(gè)比其它所有的都更重要异逐。
它們是: Ext.data.Model / Store /Ext.data.proxy.Proxy
這三個(gè)類幾乎被所有的應(yīng)用程序使用飒焦,并且得到了許多周圍類來支持典奉。
Models模型
數(shù)據(jù)包的核心就是Ext.data.Model.一個(gè)模型就在應(yīng)用程序中代表了一個(gè)實(shí)體,舉個(gè)例子榨呆,一個(gè)電子商務(wù)應(yīng)用程序可能需要有用戶罗标、商品和訂單,最基本的層次上來說积蜻,一個(gè)模型就定義了一系列的字段和其相關(guān)聯(lián)的業(yè)務(wù)邏輯闯割。
我們可以看一下模型的幾個(gè)主要部分:
Fields/ Proxies/ Validations/Associations
創(chuàng)建一個(gè)模型
通常來說,使用一個(gè)基類來定義你的模型是一個(gè)好的選擇竿拆。這個(gè)基類可以方便你在一個(gè)地方對你的模型進(jìn)行特定的配置宙拉,這樣也方便配置schema,schema是你應(yīng)用程序所有模型的管理器丙笋,現(xiàn)在我們可以先重點(diǎn)關(guān)注下兩個(gè)比較有用的配置選項(xiàng):
Ext.define('MyApp.model.Base',{
??? extend: 'Ext.data.Model',
??? fields: [{
??????? name: 'id',
??????? type: 'int'
??? }],
??? schema: {
??????? namespace: 'MyApp.model',? // generate auto entityName
??????? proxy: {???? // Ext.util.ObjectTemplate
??????????? type: 'ajax',
??????????? url: '{entityName}.json',
??????????? reader: {
??????????????? type: 'json',
??????????????? rootProperty:'{entityName:lowercase}'
??????????? }
??????? }
??? }
});
不同的應(yīng)用程序在基類模型中會有不同的內(nèi)容谢澈,尤其是fields.
Proxies代理
代理是在Models和Stores中使用的煌贴,它負(fù)責(zé)加載和保存模型的數(shù)據(jù)。有兩種不同類型的代理:客戶端和服務(wù)端(Client And Server).
代理可以直接在基類model的schema中定義锥忿,比如上面的代碼例子牛郑。
客戶端代理
客戶端的例子有Memory和Local
Storage,使用了HTML5的localstorage特性敬鬓。盡管舊的瀏覽器不支持這些新的HTML5 AP淹朋,但是它在實(shí)際應(yīng)用中是非常有用的。
服務(wù)端代理
服務(wù)端代理負(fù)責(zé)封裝遠(yuǎn)程服務(wù)的數(shù)據(jù)钉答,例子有AJAX/JSONP/REST础芍。
Schema
Schema是一系列有彼此關(guān)聯(lián)的實(shí)體。當(dāng)一個(gè)模型配置了schema,所有的衍生類都會繼承数尿。上述例子中仑性,schema已經(jīng)默認(rèn)給所有的模型配置了兩個(gè)默認(rèn)屬性。
第一個(gè)配置是namespace命名空間右蹦,通過指定這個(gè)命名空間诊杆,所有實(shí)體都擁有了一個(gè)縮略名,縮略名在定義實(shí)體關(guān)聯(lián)關(guān)系的時(shí)候非常重要嫩实。
第二個(gè)配置是 代理proxy刽辙,這是一個(gè)類模板窥岩,跟Ext.XTemplete有點(diǎn)類似甲献。不同的就是類模板在給定數(shù)據(jù)的時(shí)候產(chǎn)生的是對象,在此指定之后颂翼,沒有顯示指定proxy的都會使用該proxy.
應(yīng)用程序中很可能會存在這種微小差異但又絕大多數(shù)相同的請求晃洒,通過這樣配置,可以減少每個(gè)模型直接的重復(fù)定義proxy朦乏。
User.json被我們指定為url:{entityName.json}球及,應(yīng)該返回JSON字符串。在例子中我們使用的是
{
? "success": "true",
? "user": [
??? {
????? "id": 1,
????? "name": "Philip J.Fry"
??? },
??? {
????? "id": 2,
????? "name": "HubertFarnsworth"
??? },
??? {
????? "id": 3,
????? "name": "TurangaLeela"
??? },
??? {
????? "id": 4,
????? "name": "Amy Wong"
??? }
? ]
}
Stores
通常情況下model模型會配合store一起使用呻疹,store是一系列的records記錄的集合吃引,而record是一個(gè)模型驅(qū)動的類的實(shí)例,下面的例子是一個(gè)創(chuàng)建store和加載數(shù)據(jù)的:
var store = new Ext.data.Store({
??? model: 'MyApp.model.User'
});
store.load({
??? callback:function(){
??????? var first_name =this.first().get('name');
???????? console.log(first_name);
??? }
});
我們手動加載store來獲取一系列的MyApp.model.User的記錄刽锤,這些記錄在加載完成后會在回調(diào)事件中打印出來镊尺。
內(nèi)聯(lián)數(shù)據(jù)Inline data
Store可以加載內(nèi)聯(lián)數(shù)據(jù),store根據(jù)我們傳入的對象加載到模型適合的類型中去并思。
new Ext.data.Store({
??? model: 'MyApp.model.User',
??? data: [{
??????? id: 1,
??????? name: "Philip J. Fry"
??? },{
??????? id: 2,
??????? name: "Hubert Farnsworth"
??? },{
??????? id: 3,
??????? name: "Turanga Leela"
??? },{
??????? id: 4,
??????? name: "Amy Wong"
??? }]
});
排序和分組
Stores可以執(zhí)行排序庐氮,分組和過濾,可以是本地的也可以是服務(wù)端的宋彼。
new Ext.data.Store({
??? model: 'MyApp.model.User',
??? sorters: ['name','id'],
??? filters: {
??????? property: 'name',
??????? value??: 'Philip J. Fry'
??? }
});
在這個(gè)store中弄砍,數(shù)據(jù)通過name和id排序仙畦,數(shù)據(jù)會過濾只加載包含Philip J. Fry的用戶,通過API可以隨時(shí)對這些屬性進(jìn)行調(diào)整修改音婶。
Associations關(guān)聯(lián)關(guān)系
模型可以通過關(guān)聯(lián)關(guān)系A(chǔ)PI聚合到一起慨畸,多數(shù)應(yīng)用程序會處理很多模型,并且模型直接存在關(guān)系衣式。一個(gè)授權(quán)博客程序可能會有用戶和發(fā)稿先口,每一個(gè)用戶創(chuàng)建很多稿件,所以一個(gè)用戶可能會存在多個(gè)發(fā)稿瞳收,但是一個(gè)稿件只能被一個(gè)用戶發(fā)布碉京,這就是大家熟知的多對一關(guān)系,我們在ExtJS中這樣表達(dá):
Ext.define('MyApp.model.User',{
??? extend: 'MyApp.model.Base',
??? fields: [{
??????? name: 'name',
??????? type: 'string'
??? }]
});
Ext.define('MyApp.model.Post',{
??? extend: 'MyApp.model.Base',
??? fields: [{
??????? name: 'userId',
??????? reference: 'User', // the entityNamefor MyApp.model.User
??????? type: 'int'
??? }, {
??????? name: 'title',
??????? type: 'string'
??? }]
});
在應(yīng)用程序中對豐富的關(guān)系模型進(jìn)行表述可以說是很方便了螟深。每一個(gè)模型都可以有任意數(shù)量的對其他模型的關(guān)聯(lián)關(guān)系谐宙。另外,你的模型定義的順序也可以比較隨意界弧,一旦你有了這個(gè)模型的類型凡蜻,你就可以方便的遍歷關(guān)聯(lián)數(shù)據(jù)了。比如垢箕,你想獲取一個(gè)用戶所有的發(fā)稿划栓,你就可以這樣做:
// Loads User with ID 1 andrelated posts and comments
// using User's Proxy
MyApp.model.User.load(1, {
??? callback: function(user) {
??????? console.log('User: ' +user.get('name'));
??????? user.posts(function(posts){
??????????? posts.each(function(post) {
??????????????? console.log('Post: ' +post.get('title'));
??????????? });
??????? });
??? }
});
上述代碼是說,model新增了一個(gè)方法user.posts()条获,每一個(gè)用戶都有許多發(fā)稿忠荞,調(diào)用user.posts()就會返回Post模型配置的store。
關(guān)聯(lián)關(guān)系不僅在加載數(shù)據(jù)的時(shí)候有用帅掘,在創(chuàng)建新的記錄的時(shí)候也可以用:
user.posts().add({
??? userId: 1,
??? title: 'Post 10'
});
user.posts().sync();
這個(gè)實(shí)例創(chuàng)建了一個(gè)新的發(fā)稿委煤,自動賦值用戶ID給ID字段,調(diào)用sync()方法通過proxy保存新的發(fā)稿修档,這是異步的方法碧绞,所以你可以設(shè)置當(dāng)完成操作的時(shí)候進(jìn)行回調(diào)。
逆關(guān)聯(lián)關(guān)系在Post模型中也會產(chǎn)生新的方法:
MyApp.model.Post.load(1, {
??? callback: function(post) {
??????? post.getUser(function(user) {
??????????? console.log('Got user from post: '+ user.get('name'));
??????? });??????????????????????????
??? }
});
MyApp.model.Post.load(2, {
??? callback: function(post) {
??????? post.setUser(100);????????????????????????
??? }
});
加載方法getUser()是異步的吱窝,需要一個(gè)回調(diào)來創(chuàng)建用戶實(shí)例讥邻,setUser()方法僅僅就是更新了用戶id為100并且保存了Post模型,跟以前一樣院峡,可以通過保存操作的返回成功與否觸發(fā)回調(diào)兴使。
加載嵌入數(shù)據(jù)
當(dāng)關(guān)聯(lián)關(guān)系定義好之后,單請求可以同時(shí)請求到關(guān)聯(lián)數(shù)據(jù)撕予,比如鲫惶,看下面的返回值;{
??? "success": true,
??? "user": [{
??????? "id": 1,
??????? "name": "Philip J.Fry",
??????? "posts": [{
??????????? "title": "Post1"
??????? },{
??????????? "title": "Post2"
??????? },{
??????????? "title": "Post3"
??????? }]
??? }]
}
通過單請求实抡,可以同時(shí)獲取用戶和用戶下的文章欠母,兒不必先請求用戶欢策,再根據(jù)用戶獲取下面的發(fā)稿。
驗(yàn)證
模型同時(shí)也提供了對數(shù)據(jù)校驗(yàn)的一大群支持赏淌,為了進(jìn)行演示踩寇,我們通過上述例子進(jìn)行構(gòu)造數(shù)據(jù),我們給用戶模型增加了一些數(shù)據(jù)校驗(yàn):
Ext.define('MyApp.model.User',{
??? extend: 'Ext.data.Model',
??? fields: ...,
??? validators: {
??????? name: [
??????????? 'presence',
??????????? { type: 'length', min: 7 },
??????????? { type: 'exclusion', list: ['Bender']}
??????? ]
??? }
});
校驗(yàn)定義一個(gè)name字段六水,對該字段進(jìn)行校驗(yàn)俺孙,校驗(yàn)規(guī)則就是name字段下配置的對象,例子說的是對name字段進(jìn)行校驗(yàn)掷贾,校驗(yàn)規(guī)則是長度不得少于7個(gè)字符睛榄,數(shù)據(jù)可以是除了Bender之外的任何數(shù)據(jù)。
有些嚴(yán)重包含其他一些配置選擇想帅,比如長度可以有最長和最短场靴,max min屬性,格式化可以使用matcher等港准,ExtJS提供了五個(gè)校驗(yàn)旨剥,用戶自定義校驗(yàn)也很簡單:
Presence-確保該字段必須存在值,不允許有空字符串浅缸。
Length-確保字符串有正確的長度轨帜,最大最小都是可選的。
Format-確保字符串滿足正則表達(dá)式的格式衩椒。
Inclusion-確保值包含我們要求的蚌父。
Exclusion-確保值不包含我們要求的。
下面可以通過用戶實(shí)例來嘗試使用校驗(yàn)烟具,我們創(chuàng)建一個(gè)用戶梢什,進(jìn)行校驗(yàn)的:
// now lets try to create anew user with as many validation
// errors as we can
var newUser = newMyApp.model.User({
??? id: 10,
??? name: 'Bender'
});
// run some validation on thenew user we just created
console.log('Is User valid?',newUser.isValid());
//returns 'false' as therewere validation errors
var errors =newUser.getValidation(),
??? error?= errors.get('name');
console.log("Error is:" + error);
關(guān)鍵方法就是getValidation(),通過該方法進(jìn)行校驗(yàn)和返回校驗(yàn)結(jié)果朝聋,校驗(yàn)記錄是懶加載的并且需要的時(shí)候才更新。
上面的結(jié)果是:Length must be greater than 7
所以我們提供一個(gè)大于7個(gè)字符的字符:
newUser.set('name', 'BenderBending Rodriguez');
errors =newUser.getValidation();
這樣囤躁,所有條件都被滿足了冀痕,調(diào)用newUser.isValid()返回true,當(dāng)我們調(diào)用getValidation()就能保證字段都是正常的了。