Extjs4中的store


  Extjs 4引入新的數據包,其中新增了不少新類並對舊有的類作出了修整。使數據包更強大和更容易使用。
 本章我們將學習一下內容:
2.1. 概述新特性                                    
     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)。
 
2.2. 新Model類                                    
     一個Model代表一個實體、一個對象;並包含一系列的方法和字段來操作實體或對象的數據。Model類類似於舊有版本的Extjs中用於創建字段和方法來操作數據的Record類。但Model類更強大,我們可以創建多個Model子類並通過關聯(Associations)將它們連接在一起,也可以通過合法性驗證(Validations)來對數據進行驗證,同時我們還可以通過Proxy來讓Model子類直接從服務端讀寫數據。下面的圖表展現出Model的主要功能:
2.2.1. 字段聲明                                    
創建一個具體的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:初始化排序方向(選填)
     sortType:
     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", {
          extend: "Model",
          fields: [{
               name: "b", type: "int", convert: function(val, record){
                    return 1 + record.get("a");
               }
          }, {
               name: "a", defaultValue: "1", type: "int"
          }]
     });
     var instance = new A(); 此時就會拋出異常。
(選填,若不設置則使用字段數據類型的默認convert)
 
現在我們已經學會在Model中定義字段了,下面我們繼續探討在Model中定義方法,並在方法中操作字段吧!首先直接上代碼:
Ext.define("A", {
     extend: "Ext.data.Model",
     fields: [{
          name: "key", defaultValue: 2, type: "int"
     }],
     showKey: function(){
          return this.get("key");
     }
});
var a = new A();
a.showKey(); // 結果為key
 
2.2.2. 合法性驗證
     合法性驗證是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();
下面我們來具體了解這六種驗證規則
  1. presence:用於設置字段為必填項(0為有效值,空字符串未無效值),若字段沒有值或為無效值則不通過驗證;
  2. length:用於設置字段值的最短和最長長度,附加屬性為min和max;
  3. format:用於設置字段值必須符合的格式,附加屬性為matcher:正則表達式;
  4. inclusion:用於設置字段值必須為列表中的一項,附加屬性list:候選值數組;
  5. exclusion:用於設置字段值必須不為列表中的一項,附加屬性list:排除值數組;
  6. 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屬性。具體如下:   
Ext.define("Patient", {
          extend: "Ext.data.Model",
          fields: [......],
          validations: [{
               field: "age", type: "presence", message: "請填寫年齡!"
          },......}]
     }); 
注意:局部失敗提示語的優先級高於全局失敗提示語。
     定義失敗提示語后,我們就要執行合法性驗證並獲取驗證結果了。
     我們可以通過"Model子類實例化對象.validate()"來執行合法性驗證,該方法將返回Ext.data.Errors的實例,它就是用於封裝合法性驗證結果的類。以下是常用的屬性和方法:
     length:驗證失敗信息個數
     isValid():合法性驗證是否通過
     getByField():通過Model的字段名來獲取該字段的驗證失敗信息,對於一個字段可以有多個驗證失敗信息。格式:[{field: "字段名", message: "失敗提示語"},.......]
 
2.2.3. 通過proxies和stores加載和保存數據                        
     Extjs4中我們可以通過在Model中設置proxy配置項,來實現從服務端加載和保存數據。代碼如下:
Ext.define("Blog", {
     extend: "Ext.data.Model",
     fields: [
          {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": "fsjohnhuang@hotmail.com"
     }]
}
 
2. 修改記錄內容
blog.set("name", "john huang");
blog.save({
     success: function(){
          console.log("success!");
     }
});
 
3. 刪除記錄
blog.destroy({
     success: function(){
          console.log("success!");
     }
});
 
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",
     proxy: {
          type: "rest",
          url: "data/blogs",
          format: "json",
          reader: {
               type: "json",
               root: "blogs"
          }
     }
});
store.load(function(records){
     ..............
});
可以看到在通過Store的model配置項即可將指定的Model子類附加到Store中;
而Store中同樣能設置proxy屬性,這樣做的好處是即使加載的url不同也能重用Model子類;
"model子類.load()"是加載單條記錄,而"Store子類實例.load()"是加載多條記錄。
 
2.2.4. 通過關聯(associations)建立model間的聯系                      
     當我們開發實際應用時,需要建立多個model子類,而他們之間或多或少會存在某種關聯。在舊版的Extjs中,我們無法通過Record類來設置關聯關系,而Extjs4 的Model類就可以通過associations(Ext.data.Association)來建立一對多、一對一的關聯關系(多對多的關聯實際是通過兩個一對多的關聯來組成的)
     這里有三種類型的Associations:
  1. Ext.data.association.HasMany(Model.hasMany屬性與之對應):用於表示一對多的關系
  2. Ext.data.association.BelongsTo(Model.belongsTo屬性與之對應):用於表示多對一的關系
  3. Ext.data.association.HasOne(Model中沒有與之直接對應的屬性,要用):用於表示一對一的關系
下面通過實例來理解吧!
     現在有Author、Book和Chapter三個表。一個author可以寫多本book,一本book可以有多個章節。(譯者語:下面我們只關注Author和Book的關聯關系,因為Book和Chapter的關聯關系與之相同就不重復敘述了)
 
2.2.4.1. 使用Ext.data.Model.hasMany屬性來建立一對多關系
     Ext.define("Author", {
          extend: "Ext.data.Model",
          requires: ["book"],
          fields: [{
               name: "id", type: "int"
          }, {
               name: "name", type: "string"
          }],
          hasMany: {
               // filterProperty: "name",
               model: "Book",
               foreignKey: "authorId",
               primaryKey: "id",
               name: "books",
               autoLoad: true
          }
     });
     
    Ext.define("Book", {
          extend: "Ext.data.Model",
          fields: [{
               name: "id", type: "int"
          }, {
               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的配置項:
  1. associatedModel(就是上述的model)(必填):指定關聯Model子類(即實例中的Book)
  2. ownerModel(必填,通過Ext.data.Model.hasMany屬性設置時,自動設置為當前Model子類):指定主Model子類(即實例中的Author)
  3. name(選填):設置獲取關聯Model子類實例化對象集(數據類型為Ext.data.Store)的方法名稱,默認為"關聯Model子類小寫s",如Book的該方法名為books;
  4. foreignKey(選填):設置關聯Model子類中與主Model子類主鍵對應的外鍵字段名稱,默認為"主Model子類名稱小寫_id",如Book中應存在author_id字段;
  5. primaryKey(選填):設置主Model子類中與關聯Model子類外鍵對應的主鍵字段名稱,默認為"id"。API文檔中描述其默認值為Ext.data.Model.idProperty的值(用於設置Model的主鍵字段名稱,默認為"id"),但實踐證明即使修改了Ext.data.Model.idProperty,primaryKey依舊為"id"。
  6. autoLoad(選填):
    1. 在執行上述代碼中"var books = author.books();"時是否向服務端發起獲取Book數據的請求,true為發起,false為不發起。默認值為false。
    2. 在執行"Author.load(1,function(record){.......});"時是否向服務端發起獲取Book數據的請求,true為發起,false為不發起。默認值為false。
  7. associationKey(選填):設置從哪個字段讀取關聯Model子類的數據,默認為"關聯Model子類的名稱小寫s"。(多數用於自關聯或嵌套結構的數據中,詳情請查看下面的“通過Store我們可以一次性加載所有數據”內容)
  8. 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"
     });
     books.add({
          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,

     proxy: {
          type: "ajax",
          format: "json",
          url: "getDummyData.ashx",
          reader: {
               type: "json",
               root: "data"
               }
          }

});
Ext.define("Author", {
          extend: "Ext.data.Model",
          requires: ["Book"],
          fields: [{
               name: "id", type: "int"
          }, {
               name: "name", type: "string"
          }],
          hasMany: {
               //autoLoad: true,               
               model: "Book",
               foreignKey: "authorId",
               primaryKey: "id",
               name: "books",
               associationKey: "childData" // 默認為books
          }
     });
Ext.define("Book", {
          extend: "Ext.data.Model",
          fields: [{
               name: "id", type: "int"
          }, {
               name: "title", type: "string"
          }, {
               name: "authorId", type: "int"
          }],
          proxy: {
               type: "ajax",
               format: "json",
               url: "getDummyData.ashx",
               reader: {
                    type: "json",
                    root: "datas"
               }
          }
     });
 
服務端返回的數據格式為
{\"data\":[
     \"id\":1,
     \"name\": "John Huang",
     \"childData\": [{
          \"id\": 1,
          \"title\": \"Javascript\",
          \"authorId\": 1
     }, {
          \"id\": 2,
          \"title\": \"Javascript cookbook\",
          \"authorId\": 1
     }]
]}
 
此時實例化Store對象時就會一次性獲取獲取author和book數據。而book的數據就用author的associationKey設置的值(childData)來標識映射關系。此時如果b中的hasMany屬性中設置了autoLoad為true,那么除了Store發起請求外,還會觸發Book向服務端發起數據請求。
 
注意:我想加載也許都會注意到上述幾段代碼中都發現出現Ext.syncRequire和requires的語句,這是因為設置Ext.data.Store.model和Ext.data.Association.associatedModel均為類文件同步加載,關於類文件同步加載的內容請參考第一章。
 
2.2.4.2. 使用Ext.data.Model.belongsTo屬性來建立多對一關系
 Ext.define("Author", {
          extend: "Ext.data.Model",
          requires: ["book"],
          fields: [{
               name: "id", type: "int"
          }, {
               name: "name", type: "string"
          }],
          proxy: {
               type: "ajax",
               format: "json",
               url: "getDummyData.ashx",
               reader: {
                    type: "json",
                    root: "datas"
               }
          }
     });
     
    Ext.define("Book", {
          extend: "Ext.data.Model",
          requires: ["Author"],
          fields: [{
               name: "id", type: "int"
          }, {
               name: "title", type: "string"
          }, {
               name: "authorId", type: "int"
          }],
          belongsTo: {
               model: "Author",
               primaryKey: "id",
               foreignKey: "authorId",
               getterName: "getA",
               setterName: "setA"
          }
     });
 
     var book = Ext.create("Book", {
          id: 1,
          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的配置項:
  1. associatedModel(就是上述的model)(必填):指定關聯Model子類(即實例中的Author)
  2. ownerModel(必填,通過Ext.data.Model.hasMany屬性設置時,自動設置為當前Model子類):指定主Model子類(即實例中的Book)
  3. foreignKey(選填):設置主Model子類中與關聯Model子類主鍵對應的外鍵字段名稱,默認為"關聯Model子類名稱小寫_id",如Book中應存在author_id字段;
  4. primaryKey(選填):設置關聯Model子類中與主Model子類外鍵對應的主鍵字段名稱,默認為"id"。API文檔中描述其默認值為Ext.data.Model.idProperty的值(用於設置Model的主鍵字段名稱,默認為"id"),但實踐證明即使修改了Ext.data.Model.idProperty,primaryKey依舊為"id"。
  5. getterName(選填):設置獲取關聯Model子類對象的函數名稱,默認為"get關聯Model子類名稱",如getAuthor。該方法為異步函數;
  6. setterName(選填):設置設置關聯Model子類對象的函數名稱,默認為"get關聯Model子類名稱",如setAuthor。該方法調用時要注意:
    1. 調用該方法僅僅修改Model子類間的對應關系,而無法修改關聯Model子類的字段值,等同於book.set("authorId", 2);
    2. 若調用時只傳入一個參數,如book.setAuthor(2)。此時修改結果僅保存在客戶端而不會向服務端發起保存請求;
    3. 若調用時傳入兩個參數,如book.setAuthor(2, function(){....})。此時就會將修改結果發送到服務端;
    4. 該方法的內部實現如下: 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方法的參數配置。
  7. associationKey(選填):設置從哪個字段讀取關聯Model子類的數據,默認為"關聯Model子類的名稱小寫s"。(多數用於自關聯或嵌套結構的數據中)
現在我們可以通過hasMany和belongsTo兩個屬性來使Model子類間可雙向互操作了。
 
2.2.4.3. 使用Ext.data.Model.hasOne屬性來建立一對一關系
      Ext.define("Author", {
          extend: "Ext.data.Model",
          requires: ["book"],
          fields: [{
               name: "id", type: "int"
          }, {
               name: "name", type: "string"
          }],
          proxy: {
               type: "ajax",
               format: "json",
               url: "getDummyData.ashx",
               reader: {
                    type: "json",
                    root: "datas"
               }
          }
     });
     
    Ext.define("Book", {
          extend: "Ext.data.Model",
          requires: ["Author"],
          fields: [{
               name: "id", type: "int"
          }, {
               name: "title", type: "string"
          }, {
               name: "authorId", type: "int"
          }],
          associations: { type: 'hasOne', model: 'Author' }
     });
 
     var book = Ext.create("Book", {
          id: 1,
          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.HasOne的配置項與Ext.data.association.BelongsTo的配置項一樣。
 
2.2.5. 代理(Proxies)                                  
     代理的職責是加載和保存數據。它可以用於Store,也可以直接用於Model。
     在Extjs 3中,我們只能從服務端加載數據和將數據保存到服務端。而Extjs 4引入了三種新的代理,通過它們我們可以從客戶端加載數據和保存數據到客戶端。
     Exjts 4中含有兩類代理:客戶端代理(LocalStorageProxy、SessionStorageProxy和MemoryProxy),服務端代理(AjaxProxy、ScriptTagProxy、DirectProxy和RestProxy)。具體如下圖:
2.2.5.1.  客戶端代理
     客戶端代理使用瀏覽器存儲器,這是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靈活高效。
下面是客戶端代理的類關系圖:
  1. Ext.data.proxy.Client:是所有客戶端代理的祖先類,由Extjs框架內部使用;
  2. Ext.data.proxy.WebStorage:是Ext.data.proxy.LocalStorage和Ext.data.proxy.SessionStorage的父類,由Extjs框架內部使用;
  3. Ext.data.proxy.LocalStorage
  4. Ext.data.proxy.SessionStorage
  5. Ext.data.proxy.Memory
2.2.5.1.1. LocalStorageProxy
     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", {
     extend: "Ext.data.Model",
     fields: [{
          name: "id", type: "int"
     }, {
          name: "description", type: "string"
     }],
     proxy: {
          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實例加載數據
     var store = Ext.create("Ext.data.Store", {
          model: "UserPreference"
     });
     store.load();
     
      通過Model子類加載數據
     UserPreference.load(1,{success:function(record,operation){......}});
 
2.2.5.1.1.1 IdGenerator(Id生成器)
     (譯者語:該小節為譯者為讓大家更好地理解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", {
          extend: "Ext.data.Model",
          fields: [....],
          idgen: "sequential"
     });
     或者
      Ext.define("Book", {
          extend: "Ext.data.Model",
          fields: [....],
          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使用方式如下:
     Ext.define("Book", {
          extend: "Ext.data.Model",
          fields: [....],
          idgen: "uuid"
     });
     或者
      Ext.define("Book", {
          extend: "Ext.data.Model",
          fields: [....],
          idgen: {
               type: "uuid",
               id: "test",
               version: 1
          }
     });     
      配置項說明:
     1. type:使用的id生成器類型;
     2. id:設置全球唯一Id生成器的ID;
     3. version:1代表全球唯一id是基於時間的;4代表全球唯一id是通過偽隨機數的。
     
2.2.5.1.2. SessionStorageProxy 
     SessionStorage也是HTML5的新特性,用法跟LocalStorage一樣,唯一的區別是關閉瀏覽器后保存到其中的信息將被清空。
 
2.2.5.1.3. MemoryProxy 
     MemoryProxy主要用於加載內聯數據,當頁面刷新時MemoryProxy的數據將會丟失。當要加載臨時數據時,它將會是首選。
實例如下:
     Ext.define("Gender", {
          extend: "Ext.data.Model",
          fields: [{
               name: "id", type: "int
          }, {
               name: "name", type: "string"
          }]
     });
     var data = {
          genders: [{
               id: 1,
               name: "Female"
          }, {
               id: 2,
               name: "Male"
          }, {
               id: 3,
               name: "Unknown"
          }]
     };
     var store = Ext.create("Ext.data.Store", {
          autoLoad: true,
          model: "Gender",
          data: data,
          proxy: {
               type: "memory",
               reader: {
                    type: "json",
                    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
     });
結果:
 
2.2.5.2. 服務端代理
     服務端代理通過HTTP請求從web服務端讀取數據和保存數據到web服務端。下面為類圖:
  1. Ext.data.proxy.Server:是所有服務端代理的祖先類,由框架內部調用;
  2. Ext.data.proxy.Ajax:同域異步請求
  3. Ext.data.proxy.Rest:Ext.data.proxy.Ajax的擴展
  4. Ext.data.proxy.JsonP:跨域異步請求
  5. Ext.data.proxy.Direct:使用Ext.direct.Manager來發送請求
2.2.5.2.1. AjaxProxy
     AjaxProxy是最常用到的代理類,它將使用Ajax來向服務端發送讀取、保存數據的請求。相當於Extjs3中的Ext.data.HttpProxy。
     我們只需簡單地在Model、Store的proxy屬性中設定type為"ajax"就可以了。代碼如下:
     Ext.define("Book", {
          extend: "Ext.data.Model",
          fields: [.......],
          proxy: {
               type: "ajax",
               url: "getDummyData.ashx"
          }
     });
     上述的代碼與下面的代碼功能一樣:
     var ajaxProxy = Ext.create("Ext.data.proxy.Ajax", {
          url: "getDummyData.ashx",
          model: "Book",
          reader: "json"
     });
     Ext.define("Book", {
          extend: "Ext.data.Model",
          fields: [.......],
          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: "Book",
          reader: "json"
     });
     通過Model子類或Store可以加加載數據(譯者語:因操作與之前的Model、Store加載數據代碼一樣,此處省略原文內容)
     下面我們來了解AjaxProxy的配置項:
  1. filterParam:設置url查詢參數filter的鍵名,默認是為filter;設置為undefined時表示url中不存在filter的鍵名
  2. groupParam:設置url查詢參數group的鍵名,默認為group;設置為undefined時表示url中不存在group的鍵名
  3. pageParam:設置url查詢參數page的鍵名,默認為page,用於在服務端獲取特定頁碼的數據;設置為undefined時表示url中不存在page的鍵名
  4. startParam:設置url查詢參數start的鍵名,默認為start,用於在服務端分頁;設置為undefined時表示url中不存在start的鍵名
  5. limitParam:設置url查詢參數limit的鍵名,默認為limit,用於在服務端分頁;設置為undefined時表示url中不存在limit的鍵名
  6. sortParam:設置url查詢參數sort的鍵名,默認為sort;設置為undefined時表示url中不存在sort的鍵名
  7. directionParam:設置url查詢參數dir的鍵名,默認為dir,用於設置排序方向(DESC或ASC),僅當simpleSortMode為true時有效。設置為undefined時表示url中不存在dir的鍵名
  8. simpleSortMode:設置是否只允許對單個字段進行排序,默認為false(即可以對多個字段進行排序);具體實例請看下面的代碼。
  9. extraParams:通過該屬性設置的參數在每一次向服務端發起的請求中都會被發送到服務端。若請求的url中存在與之同名的參數,則會覆蓋該屬性設置的參數。
  10. api:設置CRUD操作對應各自的url。格式為{create: "具體的url",update: "具體的url",read: "具體的url",destroy: "具體的url"}
  11. url:設置CRUD操作對應統一的url。api的優先級高於url,若api中某操作對應的url為undefined,則采用url屬性的值
  12. model(必填項):設置綁定的Model子類,可以是類名字符串或類實例對象
  13. noCache:設置是否每次請求均讀取服務端數據,默認為true
  14. cacheString:設置為了每次請求均讀取服務器數據而生產的請求url參數鍵,默認為_dc。僅noCache為true時有效。
  15. batchActions:設置啟用批量操作,默認為true。批量操作就是對Store實例中的記錄作CUD后,執行“store實例.sync()”批量將操作發送到服務端。
  16. batchOrder:已以逗號作分隔符的方式設置批量操作的順序,默認為"create,update,destroy"。僅batchActions為true時有效。
  17. reader:設置解碼服務端數據的Reader,默認為"json"
  18. writer:設置編碼客戶端數據的Writer,默認為"json"
  19. timeout:設置等待服務端響應的時限(單位:毫秒),默認30000
    譯者語:在上面的代碼中我們都將代理附加到Model或Store中,初看之下覺得只要配置好代理(客戶端、服務端代理)后就能和數據源(服務端數據源或客戶端數據源localStorage等)通信,其實是框架幫我們配置了另一個十分重要的實例——Ext.data.Operation,代表在代理中執行的單個讀或寫操作,而批量操作就是由多個Ext.data.Operation實例組成,一般無需我們直接使用。除非我們直接使用Ext.data.proxy.Ajax實例的read方法,該方法第一個參數就是Ext.data.Operation實例。
     下面我們先了解Ext.data.Operation的屬性吧:
  1. action:CRUD操作名,read、update、create、destroy之一。
  2. filters:Ext.util.Filter實例數組
  3. sorters:Ext.util.Sorter實例數組
  4. limit:讀取的記錄數
  5. start:讀取的首條記錄的索引
  6. groupers:Ext.data.Grouper實例數組
  7. page:讀取的頁碼
  8. synchronous:設置是否啟用批量操作時,該Ext.data.Operation實例是否參與並發操作,否則要等待某操作執行完成時才會執行。默認為true。
  9. batch:Ext.data.Batch實例(Ext.data.Batch用於管理批量操作中的各個Ext.data.Operation實例,一般在Ext.data.proxy.Proxy內部使用,就是proxy會自動配置該項),配置時不用配置。
  10. callback: 操作完成時的回調函數
  11. 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", {
          url: "getDummyData.ashx",
          model: "Book",
          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
 
     下面我們嘗試排序和過濾吧!
     var oper = Ext.create("Ext.data.Operation", {
          action: "read",
          sorters: [
               Ext.create("Ext.util.Sorter", {
                    property: "Title",
                    direction: "DESC"
               }),
               Ext.create("Ext.util.Sorter", {
                    property: "Name",
                    direction: "ASC"
               })
          ],
          filters:  [
               Ext.create("Ext.util.Filter", {
                    property: "Pages",
                    value: "100"
               })
          ]
     });
     proxy.read(oper);
     發送到服務器的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", {
          url: "getDummyData.ashx",
          model: "Book",
          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只能向相同域名、端口號、協議、子域名的服務端發起請求。
 
2.2.5.2.2. RestProxy
     RestProxy是AjaxProxy的子類,但其是使用RESTful URLs來處理所有CRUD操作。
     RESTful URLs的基本原則是在一個基礎URL鏈接服務,使用JSON、XML或YAML編碼格式來交換數據。而Extjs中只接受JSON和XML格式,並且使用HTTP中的GET、POST、DELETE和PUT來執行CRUD操作。下面表格展現URL和CRUD操作的映射關系:
使用實例如下:
Ext.define("Book", {
     extend: "Ext.data.Model",
     fields: [....],
     proxy: {
          type: "rest",
          appendId: true,
          // format: "json",
          url: "/getDummyData"
     }
})
Ext.data.proxy.Rest有兩個特殊的屬性appendId和format。
  1. appendId:設置是否啟用自動添加記錄ID到url后面(如url為/data,記錄ID為123,那么最后的url為/data/123),默認為true;若並不是操作單條記錄,那么就不會自動添加記錄ID到url后面。
  2. 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的請求方式)
 
2.2.5.2.3. JsonP proxy
     JsonP代理用於請求不同域名下的數據。Ext.data.proxy.JsonP等同於Extjs3中的Ext.data.ScriptTagProxy類,顧名思義就是每次請求均在頁面中添加一個script標簽到DOM樹中。如使用JsonP代理請求url為http://loiane.com的數據時,就會生成<script src="http://loiane.com?callback=someCallback"></script>
     使用實例:
     Ext.define("Book", {
          extend: "Ext.data.Model",
          fields: [.....],
          proxy: {
               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框架為我們定義好了。
 
2.2.6.  Store                                      
     Store的職責是封裝Model並配置代理來讀取、保存數據。它具有排序、過濾和分組的功能。以下是相關的類圖:
     我們一般直接用到的是Ext.data.Store和Ext.data.TreeStore,而其他(除Ext.data.AbstractStore外)均為proxy的類型而框架內部設置而成,不能直接實例化使用。
     配置項說明:
  1. autoSync:true為每修改一條記錄均會馬上發送修改到數據源,false為要顯式調用store.sync()才會批量將修改發送到數據源;默認為false。
  2. remoteSort:true發送Get請求到服務端來進行排序,false客戶端排序;默認為false。
 
2.2.7. Readers                                      
     Reader類負責將從數據源(服務端或客戶端)獲取的數據(raw data)進行解碼並加載到Model實例或Store實例中。與Extjs3不同的是,Extjs4是用proxy來將Reader進行分組而不是Store。另一個不同點是Extjs3中所有的Reader均位於Ext.data包中,而Extjs4均位於Ext.data.reader包中。但配置項兩個版本是相同的,就是說Extjs4的Reader是向后兼容的。下面是類關系圖:
下面我們一起來學習Reader中主要的配置項吧!
  1. idProperty:從數據源讀取的記錄的id屬性名,默認與Model的idProperty一致;
  2. messageProperty:從數據源讀取的數據的message屬性名,默認為message;
  3. root:從數據源讀取的數據的記錄入口屬性名,默認為空字符串;
  4. successProperty:從數據源讀取的數據的success屬性名,默認為success;其值為true則表示成功,為false且存在errors屬性則表示服務端失敗,為false且無errors屬性則表示鏈接失敗。
  5. totalProperty:從數據源讀取的數據的total屬性名,默認為total;表示總記錄數目。
  6. record:(Ext.data.reader.Xml)從數據源讀取的記錄起始標簽;表示該標簽代表一條記錄。
      Ext.data.reader.Json實例
     var jsonStore = Ext.create("Ext.data.Store", {
          autoLoad: true,
          fields: [{
               name: "id", mapping: "myId", type: "int"
          }, {
               name: "name", type: "string"
          }],
          proxy: {
               type: "json",
               url: "getDummyData.ashx",
               reader: {
                    type: "json",
                    root: "Books",
                    successProperty: "s",
                    totalProperty: "t",
                    messageProperty: "m"
               }
          }
     });
 
     服務端數據:
     {
          \"t\": 2,
          \"s\": true,
          \"m\": \"成功了!\",
          \"Books\": [{
               \"myId\": 1,
               \"name\": \"John Huang\"
          }]
     }
 
      Ext.data.reader.Array實例:
     var jsonStore = Ext.create("Ext.data.Store", {
          autoLoad: true,
          fields: ["id", {
               name: "name", type: "string", mapping: 1
          }],
          proxy: {
               type: "array",
               url: "getDummyData.ashx",
               reader: {
                    type: "json",
                    root: "Books",
                    successProperty: "s",
                    totalProperty: "t",
                    messageProperty: "m",
                    idProperty: "myId"
               }
          }
     });
 
     服務端數據:
     {
          \"t\": 2,
          \"s\": true,
          \"m\": \"成功了!\",
          \"Books\": [[1,\"John Huang\"]]
     }
 
      Ext.data.reader.Xml實例:
      var jsonStore = Ext.create("Ext.data.Store", {
          autoLoad: true,
          fields: ["id", {
               name: "name", type: "string"
          }],
          proxy: {
               type: "array",
               url: "getDummyData.ashx",
               reader: {
                    type: "json",
                    root: "Books",
                    successProperty: "s",
                    totalProperty: "t",
                    messageProperty: "m",
                    record: "Book"
               }
          }
     });
 
     服務端數據:
     <t>2</t>
     <s>true</s>
     <m>成功了!</m>
     <Books>
          <Book>
               <id>1</id>
               <name>John Huang</name>
          </Book>
     </Books>
 
2.2.8. Writers                                          
     Writer的職責是發送數據到數據源(客戶端或服務端)。和Reader一樣,在Extjs4中Writer以proxy來分組。所有Writer均屬於Ext.data.writer命名空間。下面是Writer的類關系圖:
下面我們一起了解Writer的配置項吧!
  1. nameProperty:設置發送到數據源(客戶端或服務端)的鍵值對中鍵名所來源的Ext.data.Field的屬性,默認為name;
    1. 實例:
  2. writeAllFields:設置為true時將記錄的所有字段均發送到數據源(客戶端或服務端);false時則僅發送標記為modified的字段到數據源。默認值為true。注意被標記為persist為false的字段將不發送到數據源。
2.2.8.1. JsonWriter
     (譯者語:因本人覺得原文過於累贅且沒有很好地講解JsonWriter的用法,因此以下內容均為結合API文檔后的理解筆記)
     望文生義可知JsonWriter就是把Model實例的字段編碼成JSON格式字符串,並發送到數據源。因此它的配置項又在上述的配置項之上添加了兩個,下面我們來學習吧!
  1. root:設置數據的入口成員屬性名稱,默認為空字符串;實例:root為"datas",那么傳送的JSON格式字符串為{\"datas\":[{數據對象1},{數據對象2},.......]}
  2. encode:false時編碼;true時采取Ext.encode對數據進行編碼,默認為false。注意,該屬性僅在設置了root屬性的情況下生效,因為encode為true時會對"[數據對象1,數據對象2,.......]"整體進行編碼,那么數據源端僅能通過root屬性值來獲取已編碼的數據字符串。
  3. allowSingle:false時要求數據必須被包含在數組內,默認為true;實例:allowSingle為true,數據的JSON格式字符串可以形如{\"datas\":{數據對象1}};為false時,必須為{\"datas\":[{數據對象1}]}
 
2.2.8.2. XmlWriter
     (譯者語:因本人覺得原文過於累贅且沒有很好地講解JsonWriter的用法,因此以下內容均為結合API文檔后的理解筆記)
      望文生義可知JsonWriter就是把Model實例的字段編碼成XML格式字符串,並發送到數據源。因此它的配置項又在上述的配置項之上添加了其他配置項,下面我們來學習吧!
  1. defaultDocumentRoot:設置默認的xml文檔的頂級節點標簽名稱,默認為xmlData;當documentRoot為空時生效。
  2. documentRoot:設置xml文檔的頂級節點標簽名稱,默認為xmlData;
  3. record:設置單條數據記錄的外層節點標簽名稱,默認為record;
  4. header:設置xml文檔的頭信息,如版本號和編碼方式,默認為空字符串。
注意:當數據源位於客戶端是不要使用XmlWriter。
 
2.2.9. Sorting                                                
     Store帶有記錄排序的功能(譯者語:經過上幾節內容的學習,我想大家都清楚Store的記錄排序功能其實是通過Ext.data.proxy.Proxy子類和Ext.data.Operation類實現的)。在Extjs3中我們使用sortInfo來配置排序規則,可在客戶端或服務端執行排序操作。在Extjs4中每個排序規則均為一個獨立的Ext.util.Sorter實例對象。我們即使在Store實例化階段配置好了排序規則,在之后的使用中也能改變排序規則。
     下面我們來看一下實例吧:
Ext.define("Book", {
     extend: "Ext.data.Model",
     fields: [{
          name: "id",
          type: "int"
     }, {
          name: "title", 
          type: "string"
     }, {
          name: "pages", type:"int"
     }],
     proxy: {
          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和為其配置排序規則:
var store = Ext.create("Ext.data.Store", {
     model: "Book",
     sorters: [{
          property: "pages",
          direction: "DESC"
     },{
          property: "title",
          direction: "ASC"
     }]
});
或者
var store = Ext.create("Ext.data.Store", {
     model: "Book",
     sorters: [Ext.create("Ext.util.Sorter",{
          property: "pages",
          direction: "DESC"
     }),Ext.create("Ext.util.Sorter",{
          property: "title",
          direction: "ASC"
     })]
});
 
這樣在數據加載的時候就會對數據進行客戶端排序
若想在其他時候對數據進行客戶端排序,可如下:
store.sort("pages", "ASC");
或者
store.sort([{
          property: "pages",
          direction: "ASC"
     },{
          property: "title",
          direction: "DESC"
     }]);
 
若要執行服務端排序,則Store需要配置remoteSort屬性為true(默認為false)
服務端排序,則會以GET方式向服務端發送請求,請求的URL中帶排序信息查詢參數。
 
2.2.10. Filtering                                          
      Store帶有記錄過濾的功能(譯者語:經過上幾節內容的學習,我想大家都清楚Store的記錄過濾功能其實是通過Ext.data.proxy.Proxy子類和Ext.data.Operation類實現的)。可在客戶端或服務端執行數據過濾操作。在Extjs4中每個排序規則均為一個獨立的Ext.util.Filter實例對象。我們即使在Store實例化階段配置好了過濾規則,在之后的使用中也能改變過濾規則。
     實例如下:
Ext.define("Book", {
     extend: "Ext.data.Model",
     fields: [{
          name: "id",
          type: "int"
     }, {
          name: "title", 
          type: "string"
     }, {
          name: "pages", type:"int"
     }],
     proxy: {
          type: "ajax",
          url: "data/books/books.json"
     }
});
 
var store = Ext.create("Ext.data.Store", {
     model: "Book",
     filters: [{
          property: "pages",
          value: "23"
     },{
          property: "title",
          value: "Extjs"
     }]
});
或者
var store = Ext.create("Ext.data.Store", {
     model: "Book",
     filters: [Ext.create("Ext.util.Sorter",{
          property: "pages",
           value: "23"
     }),Ext.create("Ext.util.Sorter",{
          property: "title",
          value: "Extjs"
     })]
});
 
這樣在數據加載的時候就會對數據進行客戶端數據過濾
若想在其他時候對數據進行客戶端數據過濾,可如下:
store.filter("pages", "32");
或者
store.filter([{
          property: "pages",
          value: "1111"
     },{
          property: "title",
          value: "Ext"
     }]);
若要執行服務端數據過濾,則Store需要配置remoteFilter屬性為true(默認為false)
服務端數據過濾,則會以GET方式向服務端發送請求,請求的URL中帶排序信息查詢參數。
 
2.2.11. 總結                                      
     本章向大家介紹了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框架中關於數據存儲、讀寫、排序、過濾的知識。希望大家已經對此有了基本的了解吧!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM