第二章 全新的資料包
extjs 4引入新的資料包,其中新增了不少新類并對舊有的類作出了修整。使資料包更強大和更容易使用。
本章我們将學習一下内容:
<a href="http://www.cnblogs.com/fsjohnhuang/archive/2013/02/07/2909016.html#a2.1">2.1. 概述新特性</a>
<a href="http://www.cnblogs.com/fsjohnhuang/archive/2013/02/07/2909016.html#a2.2">2.2. 新model類</a>
<a href="http://www.cnblogs.com/fsjohnhuang/archive/2013/02/07/2909016.html#a2.2.3">2.2.3. 通過proxies和stores加載和儲存資料</a>
<a href="http://www.cnblogs.com/fsjohnhuang/archive/2013/02/07/2909016.html#a2.2.4">2.2.4. 通過關聯(associations)建立model間的聯系</a>
<a href="http://www.cnblogs.com/fsjohnhuang/archive/2013/02/07/2909016.html#a2.2.5">2.2.5. 代理(proxies)</a>
<a href="http://www.cnblogs.com/fsjohnhuang/archive/2013/02/07/2909016.html#a2.2.6">2.2.6. store</a>
<a href="http://www.cnblogs.com/fsjohnhuang/archive/2013/02/07/2909016.html#a2.2.8">2.2.8. writers</a>
<a href="http://www.cnblogs.com/fsjohnhuang/archive/2013/02/07/2909016.html#a2.2.9">2.2.9. sorting</a>
<a href="http://www.cnblogs.com/fsjohnhuang/archive/2013/02/07/2909016.html#a2.2.10">2.2.10. filtering</a>
<a href="http://www.cnblogs.com/fsjohnhuang/archive/2013/02/07/2909016.html#a2.2.11">2.2.11. 總結</a>
extjs4的資料包引入了如model類的新特性。同時對store和proxy類進行了修整。大部分的修整都是向後相容的。最大的變化是在record、store和proxy類中。extjs4的資料包是其中一個與sencha touch共享的包。

model是資料包中其中一個最重要的類。它的前身是record類。現在我們可以通過model類來代表現實對象了,其中可以運用關聯
(associations)和合法性驗證(validations)。以下的表格羅列出extjs3的record與extjs4的model的異同:
現在proxy能夠直接附加到model和store中。proxy接收負責讀寫和對從服務端接收或發送到服務端的資料做編碼的reader和
writer的執行個體化對象。extjs4引入了用戶端、伺服器端的proxy。而store也有了新特性,如排序(sorting)、過濾
(filtering)和分組(grouping)。
一個model代表一個實體、一個對象;并包含一系列的方法和字段來操作實體或對象的資料。model類類似于舊有版本的extjs中用于建立字段和方
法來操作資料的record類。但model類更強大,我們可以建立多個model子類并通過關聯(associations)将它們連接配接在一起,也可以
通過合法性驗證(validations)來對資料進行驗證,同時我們還可以通過proxy來讓model子類直接從服務端讀寫資料。下面的圖表展現出
model的主要功能:
建立一個具體的model類,我們必須通過ext.define來建立一個繼承ext.data.model的新類,并定義所需的字段,如下:
ext.define("patient", {
extend: "ext.data.model",
fields: [
{name: "name"},
{name: "age", type: "int"},
{name: "phone", type: "string"},
{name: "gender", type: ext.data.types.string},
{name: "birthday", type: "date", dateformat: "d/m/y"},
{name: "alive", type: "boolean", defaultvalue: true},
{name: "weight", type: "float"},
{name: "weightkg", type: "float", convert: function(val, record){
var weightpounds = record.get("weight");
return math.round(weightpounds * 0.45459237);
}}
]
});
上面我們定義了一個名為patient的model子類。并通過fields屬性來定義patient類的字段。在講解字段(ext.data.field)之前我們先了解字段的一個重要的組成部分字段類型(ext.data.types)。
ext.data.types内置的類型有下列幾種:
其中auto為未顯示指定字段類型時的預設值。可以看到代碼中的gender字段使用ext.data.types.string,而其他字段
均使用字元串形式表示類型。其實“ext.data.types.大寫字元串”是類型類型的全名,在使用前需要已完成該類的加載。而使用字元串形式的話,
若在定義該類的時候該類型的類還沒加載,那麼就會發起類檔案同步加載的請求,待加載完成後再完成model子類的定義工作。
ext.data.types的作用是将reader從服務端讀取的資料裝換成ext.data.types指定的類型。
自定義類型:
可通過"ext.data.types.大寫字元串={convert:....,sorttype:....,tyep:...}"的形式自定義類型,具體例子如下:
ext.data.types.ucstring = {
type: "ucstring",
convert: function(val, record){
return val.touppercase();
},
sorttype: function(){
??????????
}
};
type:是類型的别名,就是在設定ext.data.field.type時的字元串。
convert:是reader從服務端讀取資料後轉換成指定資料類型的資料類型轉換函數。
sorttype:
下面我們把目光轉向ext.data.field吧。
ext.data.model中的所有字段均為ext.data.field的執行個體,ext.data.field有多個初始化配置項,下面讓我們逐一講解。
name:用于定義字段名稱(必填)
type:用于定義字段的資料類型(選填)
defaultvalue:設定字段的預設值(選填,若不填則使用字段資料類型對應的預設值)
dateformat:當字段資料類型為ext.data.types.date時,可通過該項來設定日期時間的表現格式(格式字元串與php中的相同)(選填)
mapping:用于設定model中字段與ext.data.reader.reader的子類(ext.data.reader.array、
ext.data.reader.json和ext.data.reader.xml)所讀取的資料結構中字段的對應關系。(選填,若不設定則以字段的
name屬性為依據在所讀取的資料結構中尋找同名的字段作為對應字段)
persist:用于确定若修改該字段後,該字段是否會列入ext.data.model.modified屬性中并通過ext.data.writer.writer寫到伺服器端。(選填,預設為true)
sortdir:初始化排序方向(選填)
usenull:當字段資料類型為ext.data.types.int或ext.data.types.float時,若reader讀取的資料不能轉換成數字類型,那麼usenull為true時則傳回null,否則傳回0(選填)
convert:用于設定model字段與reader讀取的資料結構的對應關系,通過定義函數function(val,
record){}來設定比mapping更複雜的對應關系。注意:因為convert是用于處理mapping無法處理的複雜對應關系,是以跟
mapping一樣是用于在reader讀取資料時或執行個體化該model子類時初始化各字段值時生效,後期再通過“model子類執行個體.set('字段名
', '值')”來修改于convert函數相關字段後并不會影響convert屬性所屬字段的值,例子如下:
ext.define("a", {
extend: "model",
fields: [{
name: "a", defaultvalue: "1", type: "int"
}, {
name: "b", type: "int", convert: function(val, record){
return 1 + record.get("a");
}
}]
});
var instance = new a();
instance.get("a"); // 結果為1
instance.get("b"); // 結果為2
instance.set("a", 10);
instance.get("a"); // 結果為10
instance.get("b"); // 結果 為2
另外值得注意的一點是,因convert函數中擷取目前操作的整條記錄,并可通過"record.get('字段名稱')"的方式擷取記錄中的某個字段,但要注意的是,擷取的字段必須是已經初始化完成的,否則無法擷取。例子如下:
ext.define("a", {
}, {
}]
var instance = new a(); 此時就會抛出異常。
(選填,若不設定則使用字段資料類型的預設convert)
現在我們已經學會在model中定義字段了,下面我們繼續探讨在model中定義方法,并在方法中操作字段吧!首先直接上代碼:
ext.define("a", {
fields: [{
name: "key", defaultvalue: 2, type: "int"
}],
showkey: function(){
return this.get("key");
}
var a = new a();
a.showkey(); // 結果為key
合法性驗證是extjs4的model類的新特性,通過該特性我們可以設定根據某些規則來定義合法性驗證。而extjs3的store類并沒有改功能。
合法性驗證(涉及的類是ext.data.validations)的聲明語句與字段的聲明語句類似,我們必須聲明驗證的類型(共6種),并且定義驗證的字段,還有其他配置項讓我們能細化驗證規則。注意:我們可以為某個字段設定多條驗證規則。例子如下:
ext.define("patient", {
extend: "ext.data.model",
fields: [......],
validations: [{
field: "age", type: "presence"
field: "name", type: "presence"
field: "name", type: "length", min: 2, max: 60
field: "name", type: "format", matcher: /([a-z ]+)/
field: "gender", type: "inclusion", list: ['m', 'f']
field: "weight", type: "exclusion", list: [0]
field: "email", type: "email"
var p = ext.create("patient", {
name: "l",
phone: "9876-5432",
gender: "unknown",
birthday: "95/26/1986"
var errors = p.validate();
errors.isvalid();
下面我們來具體了解這六種驗證規則
presence:用于設定字段為必填項(0為有效值,空字元串未無效值),若字段沒有值或為無效值則不通過驗證;
length:用于設定字段值的最短和最長長度,附加屬性為min和max;
format:用于設定字段值必須符合的格式,附加屬性為matcher:正規表達式;
inclusion:用于設定字段值必須為清單中的一項,附加屬性list:候選值數組;
exclusion:用于設定字段值必須不為清單中的一項,附加屬性list:排除值數組;
email:驗證規則format的執行個體,用于驗證字段值必須為有效的email位址。
了解了上述的驗證規則後,我們會想如果驗證失敗怎麼也要回報失敗資訊吧,那麼我們就要學習ext.data.validations和ext.data.errors了。
ext.data.validations是一個單例對象,無需建立即可直接使用。(可從其并不是以大寫字母開頭來命名看出它不是一個類,而是一個對象)
model的validations屬性使用的6種驗證規則就是對應ext.data.validations對象中的6種驗證函數。而每種驗證函數均
有一個可修改的驗證失敗提示語與其對應,如presence就對應ext.data.validations.presencemessage,這樣我們
就可以自定義錯誤提示語了。但這種方式定義的錯誤提示語是全局性的,也就是說如果我們設定
ext.data.validations.presencemessage =
"請填寫該項目!",那麼所有model子類的的presence驗證規則的失敗提示語均一樣。那如何設定專屬于某一個驗證規則的失敗提示語(局部失敗提
示語)呢?就是在定義驗證規則時,設定message屬性。具體如下:
field: "age", type: "presence", message: "請填寫年齡!"
},......}]
});
注意:局部失敗提示語的優先級高于全局失敗提示語。
定義失敗提示語後,我們就要執行合法性驗證并擷取驗證結果了。
我們可以通過"model子類執行個體化對象.validate()"來執行合法性驗證,該方法将傳回ext.data.errors的執行個體,它就是用于封裝合法性驗證結果的類。以下是常用的屬性和方法:
length:驗證失敗資訊個數
isvalid():合法性驗證是否通過
getbyfield():通過model的字段名來擷取該字段的驗證失敗資訊,對于一個字段可以有多個驗證失敗資訊。格式:[{field: "字段名", message: "失敗提示語"},.......]
extjs4中我們可以通過在model中設定proxy配置項,來實作從服務端加載和儲存資料。代碼如下:
ext.define("blog", {
{name: "id", type: "int"},
{name: "name", type: "string"},
{name: "url", type: "string"}
],
proxy: {
type: "rest",
url: "data/blogs",
format: "json",
reader: {
type: "json",
root: "blogs"
}
以上代碼,我們定義了含id、name和url三個字段的model子類blog,并配置了proxy屬性。該proxy采用restful
urls,并使用jsonreader。設定完成後,我們就可以通過model子類來實作從服務端加載和儲存資料了。具體操作如下:
1. 從服務端根據記錄id加載單條記錄
blog.load(1, {
success: function(blog){
console.log("blog: ", blog.get("url"));
服務端響應的内容如下:
{
"blogs": [{
"id": 1,
"name": "fsjohnhuang",
"url": "[email protected]"
}]
}
2. 修改記錄内容
blog.set("name", "john huang");
blog.save({
success: function(){
console.log("success!");
3. 删除記錄
blog.destroy({
4. 新增記錄
var blog = ext.create("blog", {
name: "john huang",
url: "fsjohnuang.cnblogs.com"
blog.save();
服務端必須傳回新增記錄的id資訊,服務端響應的内容如下:
"id": 2,
"name": "john huang",
"url": "fsjohnuang.cnblogs.com"
下面我們将學習如何在store中使用model吧
var store = ext.create("ext.data.store", {
model: "blog",
store.load(function(records){
..............
可以看到在通過store的model配置項即可将指定的model子類附加到store中;
而store中同樣能設定proxy屬性,這樣做的好處是即使加載的url不同也能重用model子類;
"model子類.load()"是加載單條記錄,而"store子類執行個體.load()"是加載多條記錄。
當我們開發實際應用時,需要建立多個model子類,而他們之間或多或少會存在某種關聯。在舊版的extjs中,我們無法通過record類來設定關聯
關系,而extjs4
的model類就可以通過associations(ext.data.association)來建立一對多、一對一的關聯關系(多對多的關聯實際是通
過兩個一對多的關聯來組成的)
這裡有三種類型的associations:
ext.data.association.hasmany(model.hasmany屬性與之對應):用于表示一對多的關系
ext.data.association.belongsto(model.belongsto屬性與之對應):用于表示多對一的關系
ext.data.association.hasone(model中沒有與之直接對應的屬性,要用):用于表示一對一的關系
下面通過執行個體來了解吧!
現在有author、book和chapter三個表。一個author可以寫多本book,一本book可以有多個章節。(譯者語:下面我們隻關注author和book的關聯關系,因為book和chapter的關聯關系與之相同就不重複叙述了)
ext.define("author", {
requires: ["book"],
name: "id", type: "int"
name: "name", type: "string"
}],
hasmany: {
// filterproperty: "name",
model: "book",
foreignkey: "authorid",
primarykey: "id",
name: "books",
autoload: true
});
ext.define("book", {
name: "title", type: "string"
name: "authorid", type: "int"
proxy: {
type: "ajax",
format: "json",
url: "getdummydata.ashx",
reader: {
type: "json",
root: "datas"
}
var author = ext.create("author", {
id: 1,
name: "john huang"
var books = author.books(); // 同步操作,會阻塞線程
下面我們逐一學習ext.data.association.hasmany的配置項:
associatedmodel(就是上述的model)(必填):指定關聯model子類(即執行個體中的book)
ownermodel(必填,通過ext.data.model.hasmany屬性設定時,自動設定為目前model子類):指定主model子類(即執行個體中的author)
name(選填):設定擷取關聯model子類執行個體化對象集(資料類型為ext.data.store)的方法名稱,預設為"關聯model子類小寫s",如book的該方法名為books;
foreignkey(選填):設定關聯model子類中與主model子類主鍵對應的外鍵字段名稱,預設為"主model子類名稱小寫_id",如book中應存在author_id字段;
primarykey(選填):設定主model子類中與關聯model子類外鍵對應的主鍵字段名稱,預設為"id"。api文檔中描述其預設值
為ext.data.model.idproperty的值(用于設定model的主鍵字段名稱,預設為"id"),但實踐證明即使修改了
ext.data.model.idproperty,primarykey依舊為"id"。
autoload(選填):
在執行上述代碼中"var books = author.books();"時是否向服務端發起擷取book資料的請求,true為發起,false為不發起。預設值為false。
在執行"author.load(1,function(record){.......});"時是否向服務端發起擷取book資料的請求,true為發起,false為不發起。預設值為false。
associationkey(選填):設定從哪個字段讀取關聯model子類的資料,預設為"關聯model子類的名稱小寫s"。(多數用于自關聯或嵌套結構的資料中,詳情請檢視下面的“通過store我們可以一次性加載所有資料”内容)
filterproperty(選填):設定篩選關聯model子類執行個體資料的字段名稱,預設為
ext.data.association.hasmany.foreignkey的值。注意:該屬性的值必須為主model子類和關聯model子類中
均存在的且名稱相同的字段的名稱,否則将抛異常或得不到預想的結果。下面是由主model子類擷取關聯model子類執行個體資料的分析圖,希望能讓大家更好
的了解這一屬性的作用。
上述分析圖,我們将filterproperty設定為des。注意:即使服務端傳回的資料當中存在不符合條件的資料,但因用戶端會自動執
行篩選操作,是以最後結果隻含有符合條件的資料。其實primarykey和foreignkey是filterproperty的一種變形,分析圖如
下:
通過上述的學習我們已經掌握設定一對多關系(hasmany)的内容了,既然我們可以通過如author.books()的方式(注意該方法為同步方法)來擷取關聯model子類執行個體的集合,那麼是否可以執行添加、修改、删除的操作呢?答案是肯定的。
1. 對于修改、删除操作其實就是model子類執行個體對象的修改、删除操作,方法與2.2.3.中描述的一緻;
2. 而添加操作是對store執行個體的操作,下面我們來學習一下吧!
var books = author.books();
books.add({
title: "ext js 4 first look"
title: "javascript"
books.sync();
在執行books.add()語句後,會自動設定authorid為1;然後執行books.sync()語句就将資料儲存到服務端。
通過store我們可以一次性加載所有資料(主model子類和關聯model子類的資料),執行個體如下:
ext.sycnrequire(["author","book"]);
var s = ext.create("ext.data.store", {
model: "b",
autoload: true,
type: "ajax",
url: "getdummydata.ashx",
type: "json",
root: "data"
}
}
ext.define("author", {
//autoload: true,
associationkey: "childdata" // 預設為books
ext.define("book", {
服務端傳回的資料格式為
{\"data\":[
\"id\":1,
\"name\": "john huang",
\"childdata\": [{
\"id\": 1,
\"title\": \"javascript\",
\"authorid\": 1
}, {
\"id\": 2,
\"title\": \"javascript cookbook\",
}]
]}
此時執行個體化store對象時就會一次性擷取擷取author和book資料。而book的資料就用author的associationkey
設定的值(childdata)來辨別映射關系。此時如果b中的hasmany屬性中設定了autoload為true,那麼除了store發起請求外,
還會觸發book向服務端發起資料請求。
注意:我想加載也許都會注意到上述幾段代碼中都發現出現ext.syncrequire和requires的語句,這是因為設定
ext.data.store.model和ext.data.association.associatedmodel均為類檔案同步加載,關于類檔案
同步加載的内容請參考第一章。
ext.define("author", {
requires: ["author"],
belongsto: {
model: "author",
gettername: "geta",
settername: "seta"
var book = ext.create("book", {
title: "javascript",
authorid: 1
var author = null;
// 異步操作
book.geta(function(author, operation){
author = author;
或
book.geta({
reload: true, // 強制向服務端發送資料請求,而不是讀cache内容
scope: this,
success: function(author, operation){
author = author;
},
failure: function(author, operation){
..............
callback: function(author, operation){
下面我們逐一學習ext.data.association.belongsto的配置項:
associatedmodel(就是上述的model)(必填):指定關聯model子類(即執行個體中的author)
ownermodel(必填,通過ext.data.model.hasmany屬性設定時,自動設定為目前model子類):指定主model子類(即執行個體中的book)
foreignkey(選填):設定主model子類中與關聯model子類主鍵對應的外鍵字段名稱,預設為"關聯model子類名稱小寫_id",如book中應存在author_id字段;
primarykey(選填):設定關聯model子類中與主model子類外鍵對應的主鍵字段名稱,預設為"id"。api文檔中描述其預設值
gettername(選填):設定擷取關聯model子類對象的函數名稱,預設為"get關聯model子類名稱",如getauthor。該方法為異步函數;
settername(選填):設定設定關聯model子類對象的函數名稱,預設為"get關聯model子類名稱",如setauthor。該方法調用時要注意:
調用該方法僅僅修改model子類間的對應關系,而無法修改關聯model子類的字段值,等同于book.set("authorid", 2);
若調用時隻傳入一個參數,如book.setauthor(2)。此時修改結果僅儲存在用戶端而不會向服務端發起儲存請求;
若調用時傳入兩個參數,如book.setauthor(2, function(){....})。此時就會将修改結果發送到服務端;
該方法的内部實作如下: function (value, options, scope) { if (value
&& value.ismodel) { value = value.getid(); }
this.set(foreignkey, value); if (ext.isfunction(options)) { options = {
callback: options, scope: scope || this }; } if (ext.isobject(options)) {
return this.save(options); }
},是以options的具體内容可以參考ext.data.model.save方法的參數配置。
associationkey(選填):設定從哪個字段讀取關聯model子類的資料,預設為"關聯model子類的名稱小寫s"。(多數用于自關聯或嵌套結構的資料中)
現在我們可以通過hasmany和belongsto兩個屬性來使model子類間可雙向互操作了。
ext.define("author", {
associations: { type: 'hasone', model: 'author' }
ext.data.association.hasone的配置項與ext.data.association.belongsto的配置項一樣。
代理的職責是加載和儲存資料。它可以用于store,也可以直接用于model。
在extjs 3中,我們隻能從服務端加載資料和将資料儲存到服務端。而extjs 4引入了三種新的代理,通過它們我們可以從用戶端加載資料和儲存資料到用戶端。
exjts
4中含有兩類代理:用戶端代理(localstorageproxy、sessionstorageproxy和memoryproxy),服務端代理
(ajaxproxy、scripttagproxy、directproxy和restproxy)。具體如下圖:
用戶端代理使用浏覽器存儲器,這是html5的新特性,是以并不是所有浏覽器均支援用戶端代理。下列為支援該類型代理的部分浏覽器:
ie 8.0+
firefox 3.5+
safari 4.0+
chrome 4.0+
opera 10.5+
iphone 2.0+
android 2.0+
html5的浏覽器存儲器以鍵值對的方式存儲資料(資料類型為js的原生資料類型
null,undefined,string,number,boolean),其工作方式類似于cookies。但其好處在于不會像cookies那樣
在每次請求時均在用戶端、伺服器間傳送,并且預設容量為5m且無記錄條目數限制,比cookies靈活高效。
下面是用戶端代理的類關系圖:
ext.data.proxy.client:是所有用戶端代理的祖先類,由extjs架構内部使用;
ext.data.proxy.webstorage:是ext.data.proxy.localstorage和ext.data.proxy.sessionstorage的父類,由extjs架構内部使用;
ext.data.proxy.localstorage
ext.data.proxy.sessionstorage
ext.data.proxy.memory
localstorageproxy利用html5新特性localstorage
api來從浏覽器加載資料和儲存資料到浏覽器。而localstorage是根據域名來劃分作用域,就是說每個localstorage存儲的資料均屬于
某個域名并僅在該域名下的網頁可見。localstorage是持久化存儲器,是以若不手動清理的話會一直存儲在浏覽器中,即使關掉浏覽器或重新開機系統。當
然每種浏覽器都有自己獨立的localstorage,并且彼此不能共享。
localstorage是以鍵值對得形式儲存資料,我們可以直接儲存js原始資料類型的值
(null,undefined,string,number,boolean),但不能直接儲存array、object類資料類型值。既然不能直接保
存那麼我們可以使用json.stringify(類資料類型值)将其轉換成符合json格式的字元串來儲存。其實extjs已經幫我們完成這一切了。
localstorageproxy會自動完成json的序列化和反序列化工作。下面我們通過執行個體來更好的了解吧!
ext.define("userpreference", {
name: "id", type: "int"
}, {
name: "description", type: "string"
type: "localstorage",
id: "userpreference"
1.
proxy屬性的id為必填項,用于指定儲存該model子類記錄id值的localstorage鍵名;而記錄在localstorage的鍵名由該屬
性(id)的值加上"-"後再加上記錄的id值組成;若model子類沒有設定idgen屬性(id生成器)時,就會生成一個名為該屬性(id)的值加
上"-"後再加上counter的localstorage鍵來記錄該model子類所儲存的記錄中已生成的記錄id最大值,用于當儲存一條無id的新記
錄到localstorage時為其生成一個id值;
2. 若使用store來操作localstorage,proxy屬性的id成員屬性沒有設定時,extjs會将storeid作為值作為proxy屬性的id成員屬性值。若來兩者都沒有設定那麼就會抛出異常;
3. 若浏覽器不支援localstorage,使用該proxy就會抛出異常。
通過store執行個體儲存資料
var store = ext.create("ext.data.store", {
model: "userpreference"
store.load();
store.add({description: "blue theme"});
store.add({description: "loiane groner"});
store.sync();
通過model執行個體儲存資料
var a = ext.create("userpreference", {
description: "blue theme"
a.save();
var b = ext.create("userpreference", {
description: "loiane groner"
b.save();
結果如下:
若model子類中設定了idgen屬性就不會出現userpreference-counter這一項。
通過store執行個體加載資料
通過model子類加載資料
userpreference.load(1,{success:function(record,operation){......}});
(譯者語:該小節為譯者為讓大家更好地了解model而自行添加的内容)
ext.data.model.idgen用于配置記錄id生成情況(自增、全球唯一、空),預設為空,就是設為記錄字段id的值或者該字段的預設值。
而該屬性值得類型正是ext.data.idgenerator類。ext.data.idgenerator為抽象類,其實作子類為
ext.data.sequentialidgenerator和ext.data.uuidgenerator 。
ext.data.sequentialidgenerator使用方式如下:
ext.define("book", {
fields: [....],
idgen: "sequential"
或者
ext.define("book", {
idgen: {
type: "sequential",
seed: 100,
prefix: "john"
配置項說明:
1. type:使用的id生成器類型;
2. seed:ext.data.sequentialidgenerator起始id的值,其餘的為上一個id+1;
3. prefix:id的字首,如上執行個體,id将形如:john100
ext.data.uuidgenerator使用方式如下:
idgen: "uuid"
type: "uuid",
id: "test",
version: 1
});
配置項說明:
2. id:設定全球唯一id生成器的id;
3. version:1代表全球唯一id是基于時間的;4代表全球唯一id是通過僞随機數的。
sessionstorage也是html5的新特性,用法跟localstorage一樣,唯一的差別是關閉浏覽器後儲存到其中的資訊将被清空。
memoryproxy主要用于加載内聯資料,當頁面重新整理時memoryproxy的資料将會丢失。當要加載臨時資料時,它将會是首選。
執行個體如下:
ext.define("gender", {
name: "id", type: "int
var data = {
genders: [{
id: 1,
name: "female"
id: 2,
name: "male"
}, {
id: 3,
name: "unknown"
};
autoload: true,
model: "gender",
data: data,
type: "memory",
root: "genders"
// combobox using the data store
var combobox = ext.create("ext.form.field.combobox", {
fieldlabel: "gender",
renderto: ext.getbody(),
displayfield: "name",
width: 200,
labelwidth: 50,
store: store,
querymode: "local",
typeahead: false
結果:
服務端代理通過http請求從web服務端讀取資料和儲存資料到web服務端。下面為類圖:
ext.data.proxy.server:是所有服務端代理的祖先類,由架構内部調用;
ext.data.proxy.ajax:同域異步請求
ext.data.proxy.rest:ext.data.proxy.ajax的擴充
ext.data.proxy.jsonp:跨域異步請求
ext.data.proxy.direct:使用ext.direct.manager來發送請求
ajaxproxy是最常用到的代理類,它将使用ajax來向服務端發送讀取、儲存資料的請求。相當于extjs3中的ext.data.httpproxy。
我們隻需簡單地在model、store的proxy屬性中設定type為"ajax"就可以了。代碼如下:
fields: [.......],
url: "getdummydata.ashx"
上述的代碼與下面的代碼功能一樣:
var ajaxproxy = ext.create("ext.data.proxy.ajax", {
url: "getdummydata.ashx",
model: "book",
reader: "json"
ext.define("book", {
proxy: ajaxproxy
在第一段代碼中我們隻需定義proxy屬性的type和url成員屬性即可。在第二段代碼中我們添加了model和reader,而在第一段代碼中這兩
個屬性使用了預設值,因為model子類book已經知道ajaxproxy使用哪個model子類和預設的reader是jsonreader。
當我們從服務端讀取資料時(read),代理會使用get方式進行請求,而其他請求(update、insert和delete)均用post方式。上
述的代碼設定使得各種操作(read,update,insert,delete)均請求同一個url,其實我們可以通過ajaxproxy的api屬性
設定不同的操作請求不同的url。執行個體:
var ajaxproxy = ext.create("ext.data.proxy.ajax", {
api: {
read:"getdummydata.ashx",
update: "updatedummydata.ashx",
insert: "insertdummydata.ashx",
destroy: "deletedummydata.ashx"
},
通過model子類或store可以加加載資料(譯者語:因操作與之前的model、store加載資料代碼一樣,此處省略原文内容)
下面我們來了解ajaxproxy的配置項:
filterparam:設定url查詢參數filter的鍵名,預設是為filter;設定為undefined時表示url中不存在filter的鍵名
groupparam:設定url查詢參數group的鍵名,預設為group;設定為undefined時表示url中不存在group的鍵名
pageparam:設定url查詢參數page的鍵名,預設為page,用于在服務端擷取特定頁碼的資料;設定為undefined時表示url中不存在page的鍵名
startparam:設定url查詢參數start的鍵名,預設為start,用于在服務端分頁;設定為undefined時表示url中不存在start的鍵名
limitparam:設定url查詢參數limit的鍵名,預設為limit,用于在服務端分頁;設定為undefined時表示url中不存在limit的鍵名
sortparam:設定url查詢參數sort的鍵名,預設為sort;設定為undefined時表示url中不存在sort的鍵名
directionparam:設定url查詢參數dir的鍵名,預設為dir,用于設定排序方向(desc或asc),僅當simplesortmode為true時有效。設定為undefined時表示url中不存在dir的鍵名
simplesortmode:設定是否隻允許對單個字段進行排序,預設為false(即可以對多個字段進行排序);具體執行個體請看下面的代碼。
extraparams:通過該屬性設定的參數在每一次向服務端發起的請求中都會被發送到服務端。若請求的url中存在與之同名的參數,則會覆寫該屬性設定的參數。
api:設定crud操作對應各自的url。格式為{create: "具體的url",update: "具體的url",read: "具體的url",destroy: "具體的url"}
url:設定crud操作對應統一的url。api的優先級高于url,若api中某操作對應的url為undefined,則采用url屬性的值
model(必填項):設定綁定的model子類,可以是類名字元串或類執行個體對象
nocache:設定是否每次請求均讀取服務端資料,預設為true
cachestring:設定為了每次請求均讀取伺服器資料而生産的請求url參數鍵,預設為_dc。僅nocache為true時有效。
batchactions:設定啟用批量操作,預設為true。批量操作就是對store執行個體中的記錄作cud後,執行“store執行個體.sync()”批量将操作發送到服務端。
batchorder:已以逗号作分隔符的方式設定批量操作的順序,預設為"create,update,destroy"。僅batchactions為true時有效。
reader:設定解碼服務端資料的reader,預設為"json"
writer:設定編碼用戶端資料的writer,預設為"json"
timeout:設定等待服務端響應的時限(機關:毫秒),預設30000
譯者語:在上面的代碼中我們都将代理附加到model或store中,初看之下覺得隻要配置好代理(用戶端、服務端代理)後就能和資料源(服務端資料源或
用戶端資料源localstorage等)通信,其實是架構幫我們配置了另一個十分重要的執行個體——ext.data.operation,代表在代理中執
行的單個讀或寫操作,而批量操作就是由多個ext.data.operation執行個體組成,一般無需我們直接使用。除非我們直接使用
ext.data.proxy.ajax執行個體的read方法,該方法第一個參數就是ext.data.operation執行個體。
下面我們先了解ext.data.operation的屬性吧:
action:crud操作名,read、update、create、destroy之一。
filters:ext.util.filter執行個體數組
sorters:ext.util.sorter執行個體數組
limit:讀取的記錄數
start:讀取的首條記錄的索引
groupers:ext.data.grouper執行個體數組
page:讀取的頁碼
synchronous:設定是否啟用批量操作時,該ext.data.operation執行個體是否參與并發操作,否則要等待某操作執行完成時才會執行。預設為true。
batch:ext.data.batch執行個體(ext.data.batch用于管理批量操作中的各個ext.data.operation執行個體,一般在ext.data.proxy.proxy内部使用,就是proxy會自動配置該項),配置時不用配置。
callback: 操作完成時的回調函數
scope:回調函數執行上下文對象中的this成員屬性值
filters、sorts、limit、start、groupers均在action為"read"時會将值附加到url上。
譯者語:為了更好地了解它我們就探讨一下它和ext.data.proxy.ajax的關系吧。從ext.data.proxy.ajax的屬性上我們
可以看出,ext.data.proxy.ajax設定的均是用戶端與資料源(用戶端、服務端)的關聯和url查詢參數的鍵名,我在這裡把這些歸納為設定
通信規則;而ext.data.operation用于将用戶端與資料源的具體通信資訊封裝起來,在兩者之間傳遞。
執行個體:
var proxy = ext.create("ext.data.proxy.ajax", {
startparam: "startindex"
var oper = ext.create("ext.data.operation", {
action: "read",
start: 0,
limit: 5
proxy.read(oper);
發送到伺服器的url為getdummydata.ashx?_dc=........&startindex=0&limit=5
下面我們嘗試排序和過濾吧!
action: "read",
sorters: [
ext.create("ext.util.sorter", {
property: "title",
direction: "desc"
}),
property: "name",
direction: "asc"
})
],
filters: [
ext.create("ext.util.filter", {
property: "pages",
value: "100"
]
發送到伺服器的url為getdummydata.ashx?_dc=........&sort=
[{property:"title",direction:"desc"},{property:"name",direction:"asc"}]&filter=[{property:"pages",value:"100"}]
若設定ext.data.proxy.ajax的simplesortmode配置項為true,則如下:
var proxy = ext.create("ext.data.proxy.ajax", {
startparam: "startindex",
simplesortmode: true
那麼發送到伺服器的url為getdummydata.ashx?_dc=........&sort=title&dir=desc&filter=[{property:"pages",value:"100"}]
我們可以看到sorters和filters數組均編碼為json格式的字元串,其實我們可以通過重寫ext.data.proxy.ajax的encodefilters(filters)和encodesorters(sorters)函數來自定義編碼方式。注意:一般我們認為隻能設定類在api文檔中的configs類型的成員屬性和方法,其實我們可以重寫methods類型的方法。
注意:ext.data.proxy.ajax隻能向相同域名、端口号、協定、子域名的服務端發起請求。
restproxy是ajaxproxy的子類,但其是使用restful urls來處理所有crud操作。
restful
urls的基本原則是在一個基礎url連結服務,使用json、xml或yaml編碼格式來交換資料。而extjs中隻接受json和xml格式,并且使
用http中的get、post、delete和put來執行crud操作。下面表格展現url和crud操作的映射關系:
使用執行個體如下:
fields: [....],
appendid: true,
// format: "json",
url: "/getdummydata"
})
ext.data.proxy.rest有兩個特殊的屬性appendid和format。
appendid:設定是否啟用自動添加記錄id到url後面(如url為/data,記錄id為123,那麼最後的url為/data/123),預設為true;若并不是操作單條記錄,那麼就不會自動添加記錄id到url後面。
format:設定最終url的字尾(如url為/data,format為json。那麼最終url為/data.json;若記錄id為123,那麼最終url為/data/123.json)
譯者語:因具體的crud操作與之前通過model或store的一緻,是以此處省略原文中的代碼。
注意:ext.data.proxy.rest隻能向相同域名、端口号、協定、子域名的服務端發起請求;
服務端要啟用put、delete等請求方式。(如iis中預設允許使用put、post請求方式,但沒有put和delete的請求方式)
jsonp代理用于請求不同域名下的資料。ext.data.proxy.jsonp等同于extjs3中的
ext.data.scripttagproxy類,顧名思義就是每次請求均在頁面中添加一個script标簽到dom樹中。如使用jsonp代理請求
url為http://loiane.com的資料時,就會生成<script
src="http://loiane.com?callback=somecallback"></script>
使用執行個體:
fields: [.....],
type: "jsonp",
url: "http://loianegroner.com/getdummydata.ashx",
callbackkey: "customerizekey"
book.load(2, {success:function(book, operation){
................................
}});
生成的script标簽如下:
<script src="http://loiane.com?customerizekey=somecallback"></script>
配置項講解:
callbackkey:設定回調函數名稱在url查詢參數中的鍵,預設為callback。
特别注意點:
jsonp的使用不僅需要在用戶端設定,還要服務端配合才能生效。正如上述講解那樣,jsonp其實是通過script标簽來加載異域資料,假如服務端僅
僅傳回資料,即使檢測到script加載完成也無法使用已加載的資料。是以服務端必須傳回一個以json格式資料為實參調用回調函數的完成
javascript語句作為傳回值,如somecallback({name:"john huang", age:
25});。當然somecallback這個javascript函數已經有extjs架構為我們定義好了。
store的職責是封裝model并配置代理來讀取、儲存資料。它具有排序、過濾和分組的功能。以下是相關的類圖:
我們一般直接用到的是ext.data.store和ext.data.treestore,而其他(除ext.data.abstractstore外)均為proxy的類型而架構内部設定而成,不能直接執行個體化使用。
autosync:true為每修改一條記錄均會馬上發送修改到資料源,false為要顯式調用store.sync()才會批量将修改發送到資料源;預設為false。
remotesort:true發送get請求到服務端來進行排序,false用戶端排序;預設為false。
reader類負責将從資料源(服務端或用戶端)擷取的資料(raw
data)進行解碼并加載到model執行個體或store執行個體中。與extjs3不同的是,extjs4是用proxy來将reader進行分組而不是
store。另一個不同點是extjs3中所有的reader均位于ext.data包中,而extjs4均位于ext.data.reader包中。但
配置項兩個版本是相同的,就是說extjs4的reader是向後相容的。下面是類關系圖:
下面我們一起來學習reader中主要的配置項吧!
idproperty:從資料源讀取的記錄的id屬性名,預設與model的idproperty一緻;
messageproperty:從資料源讀取的資料的message屬性名,預設為message;
root:從資料源讀取的資料的記錄入口屬性名,預設為空字元串;
successproperty:從資料源讀取的資料的success屬性名,預設為success;其值為true則表示成功,為false且存在errors屬性則表示服務端失敗,為false且無errors屬性則表示連結失敗。
totalproperty:從資料源讀取的資料的total屬性名,預設為total;表示總記錄數目。
record:(ext.data.reader.xml)從資料源讀取的記錄起始标簽;表示該标簽代表一條記錄。
ext.data.reader.json執行個體:
var jsonstore = ext.create("ext.data.store", {
name: "id", mapping: "myid", type: "int"
root: "books",
successproperty: "s",
totalproperty: "t",
messageproperty: "m"
服務端資料:
{
\"t\": 2,
\"s\": true,
\"m\": \"成功了!\",
\"books\": [{
\"myid\": 1,
\"name\": \"john huang\"
ext.data.reader.array執行個體:
fields: ["id", {
name: "name", type: "string", mapping: 1
type: "array",
messageproperty: "m",
idproperty: "myid"
服務端資料:
\"books\": [[1,\"john huang\"]]
ext.data.reader.xml執行個體:
var jsonstore = ext.create("ext.data.store", {
record: "book"
<t>2</t>
<s>true</s>
<m>成功了!</m>
<books>
<book>
<id>1</id>
<name>john huang</name>
</book>
</books>
writer的職責是發送資料到資料源(用戶端或服務端)。和reader一樣,在extjs4中writer以proxy來分組。所有writer均屬于ext.data.writer命名空間。下面是writer的類關系圖:
下面我們一起了解writer的配置項吧!
nameproperty:設定發送到資料源(用戶端或服務端)的鍵值對中鍵名所來源的ext.data.field的屬性,預設為name;
執行個體:
writeallfields:設定為true時将記錄的所有字段均發送到資料源(用戶端或服務端);false時則僅發送标記為modified的字段到資料源。預設值為true。注意被标記為persist為false的字段将不發送到資料源。
(譯者語:因本人覺得原文過于累贅且沒有很好地講解jsonwriter的用法,是以以下内容均為結合api文檔後的了解筆記)
望文生義可知jsonwriter就是把model執行個體的字段編碼成json格式字元串,并發送到資料源。是以它的配置項又在上述的配置項之上添加了兩個,下面我們來學習吧!
root:設定資料的入口成員屬性名稱,預設為空字元串;執行個體:root為"datas",那麼傳送的json格式字元串為{\"datas\":[{資料對象1},{資料對象2},.......]}
encode:false時編碼;true時采取ext.encode對資料進行編碼,預設為false。注意,該屬性僅在設定了root屬性的
情況下生效,因為encode為true時會對"[資料對象1,資料對象2,.......]"整體進行編碼,那麼資料源端僅能通過root屬性值來擷取
已編碼的資料字元串。
allowsingle:false時要求資料必須被包含在數組内,預設為true;執行個體:allowsingle為true,資料的json格式字元串可以形如{\"datas\":{資料對象1}};為false時,必須為{\"datas\":[{資料對象1}]}
望文生義可知jsonwriter就是把model執行個體的字段編碼成xml格式字元串,并發送到資料源。是以它的配置項又在上述的配置項之上添加了其他配置項,下面我們來學習吧!
defaultdocumentroot:設定預設的xml文檔的頂級節點标簽名稱,預設為xmldata;當documentroot為空時生效。
documentroot:設定xml文檔的頂級節點标簽名稱,預設為xmldata;
record:設定單條資料記錄的外層節點标簽名稱,預設為record;
header:設定xml文檔的頭資訊,如版本号和編碼方式,預設為空字元串。
注意:當資料源位于用戶端是不要使用xmlwriter。
store帶有記錄排序的功能(譯者語:經過上幾節内容的學習,我想大家都清楚store的記錄排序功能其實是通過
ext.data.proxy.proxy子類和ext.data.operation類實作的)。在extjs3中我們使用sortinfo來配置排序
規則,可在用戶端或服務端執行排序操作。在extjs4中每個排序規則均為一個獨立的ext.util.sorter執行個體對象。我們即使在store執行個體
化階段配置好了排序規則,在之後的使用中也能改變排序規則。
下面我們來看一下執行個體吧:
name: "id",
type: "int"
name: "title",
type: "string"
name: "pages", type:"int"
type: "ajax",
url: "data/books/books.json"
books.json檔案資料如下:
[{
"id": 11,
"title": "learning ext js 3.2",
"pages": 432
},{
"id": 12,
"title": "learning ext js 4.2",
"pages": 333
}]
下面我們建立一個store和為其配置排序規則:
model: "book",
sorters: [{
property: "pages",
direction: "desc"
},{
property: "title",
direction: "asc"
或者
sorters: [ext.create("ext.util.sorter",{
}),ext.create("ext.util.sorter",{
})]
這樣在資料加載的時候就會對資料進行用戶端排序
若想在其他時候對資料進行用戶端排序,可如下:
store.sort("pages", "asc");
store.sort([{
}]);
若要執行服務端排序,則store需要配置remotesort屬性為true(預設為false)
服務端排序,則會以get方式向服務端發送請求,請求的url中帶排序資訊查詢參數。
store帶有記錄過濾的功能(譯者語:經過上幾節内容的學習,我想大家都清楚store的記錄過濾功能其實是通過
ext.data.proxy.proxy子類和ext.data.operation類實作的)。可在用戶端或服務端執行資料過濾操作。在extjs4
中每個排序規則均為一個獨立的ext.util.filter執行個體對象。我們即使在store執行個體化階段配置好了過濾規則,在之後的使用中也能改變過濾規
則。
執行個體如下:
filters: [{
value: "23"
value: "extjs"
filters: [ext.create("ext.util.sorter",{
value: "23"
value: "extjs"
這樣在資料加載的時候就會對資料進行用戶端資料過濾
若想在其他時候對資料進行用戶端資料過濾,可如下:
store.filter("pages", "32");
store.filter([{
value: "1111"
value: "ext"
若要執行服務端資料過濾,則store需要配置remotefilter屬性為true(預設為false)
服務端資料過濾,則會以get方式向服務端發送請求,請求的url中帶排序資訊查詢參數。
本章向大家介紹了ext.data.model及其相關的ext.data.association、ext.data.reader、
ext.data.writer包的類、ext.data.proxy包的類、ext.data.field、ext.data.operation和
ext.data.store等extjs架構中關于資料存儲、讀寫、排序、過濾的知識。希望大家已經對此有了基本的了解吧!
轉載請标明出處哦!http://www.cnblogs.com/fsjohnhuang/archive/2013/02/07/2909016.html