程式扎記: [ ExtJS3.x 開發實戰 ] CH10 : 資料儲存與傳輸

標籤

2013年2月5日 星期二

[ ExtJS3.x 開發實戰 ] CH10 : 資料儲存與傳輸

Ext.data 簡介: 
Ext.data 在命名空間中定義了一系列 store, reader 和 proxy. Gird 與 ComboBox 都是以 Ext.data 為媒介獲取資料. 它包含非同步載入, 類型轉換, 分頁等功能. Ext.data 支援 Array, JSON 與 XML 等資料格式. 可以透過 Memory, HTTP 與 ScriptTag 等方式獲取這些格式的資料. 

Ext.data.Connection: 
Ext.data.Connection 是對 Ext.lib.Ajax 的封裝, 它提供了配置使用 Ajax 的通用方式, 它在內部透過 Ext.lib.Ajax 實現與後台的非同步呼叫. 與底層的 Ext.lib.Ajax 相比, Ext.data.Connection 提供了更簡潔的配置方式, 使用上更方便. 

Ext.data.Connection 主要用於 Ext.data.HttpProxy 和 Ext.data.ScriptTagProxy 中執行與後台互動的任務, 它會從指定的 URL 獲得資料, 並把後台傳回的資料交給 HttpProxy 或 ScriptTagProxy 處理. 底下為使用範例代碼: 
  1. var conn = new Ext.data.Connection({  
  2.     autoAbort: false,  
  3.     defaultHeaders:{  
  4.         referer: 'http://localhost:8080/'  
  5.     },  
  6.     disableCaching: false,  
  7.     extraParams:{  
  8.         name: 'name'  
  9.     },  
  10.     method: 'Get',  
  11.     timeout: 300,  
  12.     url: '01-01.txt'  
  13. });  
在使用 Ext.data.Connection 之前都要像上面這樣建立一個新的 Ext.data.Connection 實例. 我們可以在構造方法裡配置對應的參數, 比如 autoAbort 表示連結是否會自動關閉, defaultHeaders 參數表示請求的預設標頭資訊等. 

在建立的 conn 之後, 可以呼叫 request() 函數發送請求並處理返回結果, 範例程式如下: 
  1. conn.request({  
  2.     success: function(response)  
  3.     {  
  4.         Ext.Msg.alert('info', response.responseText);  
  5.     },  
  6.     failure: function()  
  7.     {  
  8.         Ext.Msg.alert('warn''failure');  
  9.     }  
  10. });  
request() 函數中可以設定 success 和 failure 兩個回呼函數, 分別在請求成功和請求失敗時呼叫. 請求成功時, success 函數的參數就是後台傳回的資訊. 而 request 函數中的參數包括: 
* url: String - 請求 url
* params: Object/String/Function - 請求傳遞的參數.
* method: String - 請求方法, 通常為 'GET' 或 'POST'.
* callback: Function - 請求完成後的回呼函數, 無論是成功或是失敗都會執行.
* success: Function - 請求成功後的回呼函數.
* failure: Function - 請求失敗後的回呼函數.
* scope: Object - 回呼函數的作用域.
* form: Object/String - 綁定的 form 表單.
* isUpload: Boolean - 是否執行檔案上傳.
* headers: Object - 請求標頭資訊.
* xmlData: Object - XML 文件物件, 可以透過 URL 附加參數方式發起請求.
* disableCaching: Boolean - 是否禁用快取, 預設為禁用.

Ext.data.Connection 還提供 abort([Number of transactionId]) 函數, 當同時有多個請求發生時, 根據指定的事物 id 放棄其中一個請求. 如果不指定事務 id, 就會放棄最後一個請求.isLoading([Number of transactionId]) 函數用法語 abort() 類似, 可以根據事務 id 判斷對應請求是否完成. 如果未指定事務 id, 就判斷最後一個請求是否完成. 

Ext.data.Record: 
Ext.data.Record 是一個設定了內部資料類型的物件, 它是 Ext.data.Store 的最基本組成部分. 如果把 Ext.data.Store 看作是一張二維表, 那麼它的每一行就對應一個 Ext.data.Record 實例.Ext.data.Record 的主要功能是保存資料, 而且在內部資料發生變動時紀錄修改狀態, 並且可以保留修改前的原始值. 

我們使用 Ext.data.Record 時通常都是由 create() 函數開始, 首先使用該函數建立一個自訂的 Record 類型, 代碼如下: 
  1. var PersonRecord = Ext.data.Record.create([  
  2.     {name: 'name', type: 'string'},  
  3.     {name: 'sex', type: 'int'}  
  4. ]);  
PersonRecord 就是我們定義的新類型, 包含字串類型的 name 與 整數類型的 sex 兩個屬性, 然後使用 new 關鍵字建立 PersonRecord 實例, 代碼如下: 
  1. var boy = new PersonRecord({  
  2.     name: 'boy',  
  3.     sex: 0  
  4. });  
現在我們得到 PersonRecord 的實例 boy, 那如何得到它上面的屬性的值呢? 下面三種方法都可以得到 boy 中 name 的屬性: 
  1. Ext.Msg.alert('Info1', boy.data.name);  
  2. Ext.Msg.alert('Info2', boy.data['name']);  
  3. Ext.Msg.alert('Info3', boy.get('name'));  
如果需要修改 boy 中的資料, 請不要使用下面方式操作: 
  1. boy.data.name = 'boy name';  
  2. boy.data['name'] = 'boy name';  
而應該使用 set() 函數, 代碼如下: 
  1. boy.set('name''boy name');  
set() 函數會判斷屬性值是否發生改變, 如果改變就要將物件的 dirty 屬性設定為 true, 並將修改前的原始值放入 modified 物件中, 供其他函數使用. 如果直接操作 data 中的值, record 就無法紀錄屬性資料的修改狀況. Record 的屬性資料被修改後, 我們可以執行以下四種操作: 
* commit() : 提交 
這個函數效果是設定 dirty 為 false, 並刪除 modified 中保存的原始資料.

* reject() : 撤銷 
這個函數效果是將 data 中已經修改的屬性值恢復成 modified 中保存的原始資料, 然後設定 dirty 為 false 並刪除保存原始資料的 modified 物件.

* getChanges(): 獲得修改的部分 
這個函數會將 data 中經過修改的屬性和資料放在一個 JSON 物件並返回. 例如上例中 getChange() 返回結果是 {name: 'boy name'}.

* isModified() : 
判斷當前的 record 中的資料是否被修改.

Ext.data.Record 還提供一個用於複製 Record 實例的函數 copy(): 
  1. var copyBoy = boy.copy();  
這樣就得到 boy 的一個副本, 包含了 boy 的 data 資料, 但 copy() 函數不會複製 dirty 和 modified 等額外的屬性值. 

Ext.data.Store: 
Ext.data.Store 是 EXT 中用來進行資料交換和資料互動的標準中介物件, 無論是 Grid 或是 ComboBox, 都是透過它實現資料讀取, 類型轉換, 排序分頁與搜尋等操作. Ext.data.Store 中有一個Ext.data.Record 陣列, 所有資料都存放在這裡, 為後面的讀取和修改做準備. 

- 基本應用 
在使用之前, 首先要建立一個 Ext.data.Store 的實例,代碼如下: 
  1. var data = [  
  2.             ['boy'0],  
  3.             ['girl'1]  
  4.             ];  
  5.   
  6. var PersonRecord = Ext.data.Record.create([  
  7.                                         {name: 'name', type: 'string'},  
  8.                                         {name: 'sex', type: 'int'}  
  9.                                     ]);  
  10.   
  11. var store = new Ext.data.Store({  
  12.     proxy: new Ext.data.MemoryProxy(data),  
  13.     reader: new Ext.data.ArrayReader({}, PersonRecord)  
  14. });  
  15.   
  16. store.load();  
每個 store 最少需要兩個元件支援, 分別是 proxy 與 readerproxy 用於從某個途徑讀取原始資料; reader 用於將原始資料傳換成 Record 實例. 這裡我們使用 Ext.data.MemoryProxy 和Ext.data.ArrayReader 將 data 陣列中的資料傳換成對應 PersonRecord 實例, 然後放入 store 中. store 建立完畢後執行 load() 實現這個傳換過程. 

- 對資料進行排序 
Ext.data.Store 提供一系列屬性和函數, 可以利用它們對資料進行操作如排序等. 在建立 Ext.data.Store 時可以使用 sortInfo 參數指定排序的欄位與排序方式. 範例代碼如下: 
  1. var store = new Ext.data.Store({  
  2.     proxy: new Ext.data.MemoryProxy(data),  
  3.     reader: new Ext.data.ArrayReader({}, PersonRecord),  
  4.     sortInfo: {field: 'name', direction: 'DESC'}  
  5. });  
這樣在 store 載入資料後, 會依據 name 欄位進行降幕排列. 對 store 呼叫 store.setDefaultSort('name', 'DESC') 也會達到同樣效果. 

或者你也可以在任何時候呼叫 sort() 函數如 store.sort('name', 'DESC') 對 store 中的資料進行排序. 如果我們希望獲取 store 的排序資訊, 可以呼叫 getSortState() 函數. 返回類似 {field: 'name' ,direction: 'DESC'} 的 JSON 物件. 

- 從 store 中獲取資料 
從 store 中獲取資料的途徑有很多種, 可以依據不同要求選擇不同函數. 最直接的方法是根據 record 在 store 中的行號獲得對應的 record, 得到了 record 就可以使用 get() 函數獲得裡面的資料了. 範例代碼如下: 
  1. store.getAt(0).get('name');  
透過下面的方式可以走遍 store 中所有 record 並依序取得它們的資料: 
  1. for(var i = 0; i < store.getCount(); i++)  
  2.     {  
  3.         var record = store.getAt(i);  
  4.         alert(record.get('name'));  
  5.     }  
除了使用 getCount() 方法外, 還可以使用 each() 函數, 代碼如下: 
  1. store.each(function(record){  
  2.     alert(record.get('name'));  
  3. });  
each() 可以接受一個函數作為參數並走遍內部所有 record, 每個 record 都會作為參數傳遞給指定的函數處理. 如果希望停止走遍, 可以隨時讓處理函數返回 false. 也可以使用 getRange() 函數獲得連續多個 record, 只需指定開始與結束位置的索引值. 參考下面範例代碼: 
  1. var records = store.getRange(01);  
  2. for(var i=0; i
  3.     {  
  4.         var record = records[i];  
  5.         alert(record.get('name'));  
  6.     }  
如果知道 record 確實的 id, 也可以根據 record 本身的 id 從 store 中獲得對應的 record. 範例代碼如下: 
  1. store.getById(1001).get('name');  
EXT 來提供函數 find() 和 findBy() 來進行資料搜尋. find() 函數定義如下: 
find(String property, String/RegExp value, [Number startIndex], [Boolean anyMatch], [Boolean caseSensitive])

在這五個參數中, 只有前兩個是必須的. 第一個參數 property 代表搜尋的欄位名稱; 第二個參數 value 是比對用字串或是正則表示式; 第三個參數 startIndex 表示從第幾行開始搜尋; 第四個參數 anyMatch 為 true 時, 不必從頭開始比對; 第五個參數 caseSensitive 為 true 會區分大小寫. 使用範例如下: 
  1. var index = store.find('name''g');  
  2. alert(store.getAt(index).get('name'));  
與 find() 函數對應的 findBy() 函數定義如下: 
findByFunction fn, [Object scope], [Number startIndex]) : Number

findBy() 函數可以讓使用者使用自定義函數對內部資料進行搜尋. fn 返回 true 時, 表示搜尋成功, 於是停止走遍並返回行號; fn 返回 false 時, 表示搜尋失敗 (未找到) 並繼續搜尋. 使用範例如下: 
  1. var index = store.findBy(function(record, id){  
  2.     return record.get('name') == 'girl' && record.get('sex') == 1;  
  3. });  
  4. alert(store.getAt(index).get('name'));  
我們還可以使用 query 與 queryBy 函數對 store 中的資料進行查詢. 與 find 與 findBy 不同的是, query 與 queryBy 返回的是一個 MixCollection 物件, 裡面包含了搜尋娶得的資料. 使用範例如下: 
  1. alert(store.query('name''boy'));  
  2. alert(store.queryBy(function(record){  
  3.     return record.get('name') == 'girl' && record.get('sex') == 1;  
  4. }));  
- 更新 store 中的資料 
可以使用 add(Ext.data.Record[] records) 向 store 末尾加入一個或多個 record. 使用的參數可以是一個或多個 record 實例. 使用範例如下: 
  1. // 添加一個  
  2. store.add(new PersonRecord({  
  3.     name: 'other',  
  4.     sex: 0  
  5. }));  
  6.   
  7. // 添加多個  
  8. store.add(new PersonRecord({  
  9.     name: 'other1',  
  10.     sex: 0  
  11. }), new PersonRecord({  
  12.     name: 'other2',  
  13.     sex: 0  
  14. }));  
add() 函數每次都會將新資料添加到 store 的尾端, 這可能破壞原先的排序. 如果希望根據原先 store 的排序設定添加新資料, 可以使用 addSorted() 函數. 它會再加入新資料後, 立即對 store 進行排序, 這樣就可以確保 store 中的資料如排序顯示. 

如果希望自己指定資料插入的索引位置, 可以使用 insert() 函數. 它的第一個參數表示插入資料的索引位置, 可以使用 record 實例或 record 實例的陣列作為第二個參數. 插入之後, 後面的資料會自動後移. 使用範例如下: 
  1. // 插入一個  
  2. store.insert(3new PersonRecord({  
  3.     name: 'other',  
  4.     sex: 0  
  5. }));  
  6.   
  7. // 插入多個  
  8. store.insert(3new PersonRecord({  
  9.     name: 'other1',  
  10.     sex: 0  
  11. }), new PersonRecord({  
  12.     name: 'other2',  
  13.     sex: 0  
  14. }));  
刪除操作可以使用 remove() 和 removeAll() 函數, 它們分別可以刪除指定的 record 和清空整個 store 中的資料: 
  1. store.remove(store.getAt(0));  
  2. store.removeAll();  
store 中沒有專門提供修改某一行 record 的操作, 我們需要先從 store 中取得一個 record 並對這個 record 內部資料進行修改, 而修改的結果會直接反映到 store 上. 範例如下: 
  1. store.getAt(0).set('name''xxx');  
- 載入及顯示資料 
store 建立後, 需要呼叫 load() 函數載入資料, 載入成功才能對 store 中的資料進行操作. load() 呼叫的完整過程如下面代碼: 
  1. store.load({  
  2.     params: {start:0, limit:20},  
  3.     callback: function(records, options, success)  
  4.     {  
  5.         Ext.Msg.alert('Info''Loading done!');  
  6.     },  
  7.     scope: store,  
  8.     add: true  
  9. });  
參數說明如下: 
* params: 是 store 載入時發送的附加參數.
* callback: 載入完畢後的回呼函式, 它包含三個參數: records 參數表示獲得的資料, options 表示執行 load() 時傳遞的參數; success 表示是否載入成功.
* scope: 指定回呼函示執行時的作用域.
* add: true 指 load() 得到的資料會加到原先 store 資料的尾端, 否則會清除之前的資料, 再將得到的資料加入到 store 中.

一般來說, 為了對 store 中的資料進行初始化, load() 函數只需要執行一次. 如果用 params 參數指定需要使用的參數, 以後再次執行 reload() 重新載入資料時, store 會自動使用上次 load() 中包含的params 參數內容. 如果有一些需要固定傳遞的參數, 也可以使用 baseParams 參數執行, 它是一個 JSON 物件, 裡面的資料會作為參數傳送給幕後處理, 範例代碼如下: 
  1. store.baseParams.start=0;  
  2. store.baseParams.limit=20;  
store 載入資料後, 有時候不需要把所有資料都顯示出來, 這時可以使用函數 filter 與 filterBy 對 store 中的資料進行過濾, 只顯示符合的部分. filter 函數定義如下: 
filterString field, String/RegExp value, [Boolean anyMatch], [Boolean caseSensitive] ): void

filter() 函數用法於之前提到的 find() 相似, 範例代碼如下: 
  1. store.filter('name''boy');  
對應的 filterBy 與 findBy 類似, 可以在自訂的函數中實現各種複雜判斷: 
  1. store.filterBy(function(record){  
  2.     return record.get('name') == 'girl' && record.get('sex') == 1;  
  3. });  
若想取消過濾並顯示所有資料, 那麼可以呼叫 clearFilter() 函數, 如果想知道 store 上是否設定了過濾條件, 可以透過 isFiltered() 函數判斷. 

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!