向原文作者致敬:http://www.ibm.com/developerworks/cn/web/wa-aj-dojogrid/
簡介
Dojo 是一個可移植的開源 JavaScript 工具包,使用它可以快速構建富用戶端 Web 應用程式。它包含的豐富的公用程式可用於構建快速響應的應用程式。而且,它還提供了許多預先封裝的開箱即用小組件,您可以通過這些小組件立即開始使用 Dojo。但是,Dojo 沒有提供文檔來詳細示範如何使用每種小組件,比如 Dojo Grid。Dojo Grid 在某種程度上就像一種可以在網頁上呈現的微型試算表。本文將從模型-視圖-控制器(MVC)設計模式的角度指導您使用 Dojo Grid 的主要功能,協助您輕鬆理解和掌握 Dojo Grid,即使您以前從未使用過它。
MVC 是軟體工程領域使用的一種架構模式。模式的成功使用可以將商務邏輯問題與使用者介面分離開來,使開發人員能夠自由修改一種商務邏輯,而不會影響到其他邏輯。這種模式的控制器通過使用者介面處理輸入事件,觸發在背景模型修改。模型操作應用程式資料,視圖使用模型來向使用者呈現結果。這種模式被廣泛應用於許多架構中,比如 Microsoft? MFC、Java Swing、Java Enterprise Edition 等。下面各節將分別介紹按 MVC 劃分的表格特性。
模型
為了區分未經處理資料與 UI 的生動外觀,Dojo Grid 維護著一個資料模型,用來儲存表格將會操作的所有未經處理資料。例如,日期/時間類型資料通常以毫秒為單位進行儲存,而不是儲存為 “2009-7-22” 等人類可讀的格式,所以可以更加輕鬆地構造並將它轉換為其他類型的日期對象。
與所有 MVC 小組件一樣,表格具有自己的資料模型,稱為資料存放區。在 Dojo 中,幾乎所有需要使用資料存放區的小組件都能夠使用 ItemFileReadStore 和 ItemFileWriteStore 等函數訪問通用資料存放區,無需使用特定於它們的資料的新 API。
ItemFileReadStore 用於讀取特定格式的 JSON 資料。DojoX 項目還提供了 XmlStore、CsvStore 和 OpmlStore 等更多儲存方式。這些儲存方式用於處理可以相應格式輸出資料的伺服器。
在 Dojo Grid 和許多其他 MVC 小組件中,所有資料通常都以項或項的屬性的形式在資料存放區中操作。通過這種方式,可以採用一種標準方式訪問資料,許多小組件可同時使用資料。清單 1 給出了本文中使用的樣本資料存放區結構。
清單 1. 簡單資料存放區結構樣本
{
identifier: 'id',
label:'name',
items: [
{id:0, name: 'Alex', manager: true, sex: 'Male', age:30, date: 1097990400000,
annualLeaveTotal: 15, annualLeaveTaken:2},
{id:1, name: 'Jack', manager: false, sex: 'Male', age:26, date: 1184995200000,
annualLeaveTotal: 10, annualLeaveTaken:3},
{id:2, name: 'Rose', manager: true, sex: 'Female', age:40, date: 894604800000,
annualLeaveTotal: 20, annualLeaveTaken:4},
{id:3, name: 'Wang', manager: true, sex: 'Male', age:44, date: 836630400000,
annualLeaveTotal: 20, annualLeaveTaken:5},
…..
}
在本樣本中:
每項都有 8 個屬性。
id 屬性是一個唯一識別碼。
資料存放區可以採用兩種方式構建:聲明為標記或以編程方式構造。
使用標記構建資料存放區
要使用標記構建資料存放區,首先需要用於有序儲存所有資料的 JSON 檔案(參見清單 2)。本文使用的是 data.json 檔案。,然後可以在 HTML 檔案中編寫清單 2 中的標記。
清單 2. 在 HTML 中聲明資料存放區
<span dojoType="dojo.data.ItemFileWriteStore" jsId="myStore"
url="data.json"></span>
接下來,將儲存分配給表格,如清單 3 所示。
清單 3. 分配表格式資料儲存
<table id="grid" jsId="grid" dojoType="dojox.grid.DataGrid" store="myStore"
rowSelector="10px">
<thead>
...
<thead>
<table>
現在,當 Dojo 解析 HTML 程式碼並構造此表格時,它建立一個 Dojo 儲存物件,該對象將從 data.json 檔案擷取資料,然後將Table Store設定為 “myStore”。圖 1 顯示了產生的表格的一個樣本。
圖 1. 構造得到的簡單表格
使用標記構建Table Store非常簡單快捷。但是,如果資料來自一台伺服器並且以動態形式組織,那麼您需要以編程方式構建表格和它的儲存。
以編程方式構建資料存放區
要動態地構造和更改表格的儲存,同時對伺服器端做出響應,您必須:
使用 JavaScript 以編程方式將傳入的資料重新組織為 Dojo 熟悉的資料。
建立一個 Dojo 儲存。
將儲存設定為表格。
清單 4 中的代碼將一個 JSON 物件建構為資料存放區格式。
清單 4. 重新組織資料
generateStoreData: function(/*JSON array*/itemList){
var data = {};
var items = [];
for (var i = 0; i < itemList.length; i++) {
var item = {};
item["id"] = itemList[i].id;
item["name"] = itemList[i].name;
item["manger"] = itemList[i].isManger;
item["sex"] = itemList[i].sex;
item["age"] = itemList[i].age;
item["date"] = itemList[i].date;
item["annualLeaveTotal"] = itemList[i].altotal;
item["annualLeaveTaken"] = itemList[i].altaken;
items.push(item);
}
data["identifier"] = "id";
data["label"] = "name";
data["items"] = items;
return data;
}
接下來,可以建立一個儲存並將其設定為表格。
清單 5. 建立並設定Table Store
dijit.byId("grid").store = new dojo.data.ItemFileReadStore({
data: this.generateStoreData(itemList)
});
所有這些步驟所得到的表格與 圖 1 所示完全一樣。
查詢資料存放區
Dojo Grid 通常在其資料模型中儲存整個資料來源。但是,隨著資料大小的不斷增長,這可能影響到效能。實際上,當 Dojo Grid 儲存中的項超出了一定數量時,並且如果每項都具有許多屬性,排序、搜尋和呈現等表格操作的效能就會顯著下降。
但是,可以通過一些方式來改善效能。可以編寫代碼來讓伺服器向瀏覽器發送有限的資料,並將這些資料構造到一個表格式資料儲存中,或者可以使用或擴充 Dojox 項目提供的 QueryReadStore 來從伺服器動態載入資料。這種方法可用於從伺服器上大型的資料存放區中檢索資料區塊。
清單 6. 使用查詢儲存來處理大型資料
<div dojoType="dojox.data.QueryReadStore" jsId="store" url="../someServlet"
requestMethod="post"></div>
<div dojoType="dojox.grid.data.DojoData" jsId="model" store="store"
sortFields="[{attribute: 'name', descending: true}]" rowsPerPage="30"> </div>
<div id="grid" jsId="grid" dojoType="dojox.grid.DataGrid" model="model" structure="layout"
rowSelector="10px"><div>
DojoX 項目提供了許多其他資料存放區來滿足不同的用途。表 1 給出了 Dojo 中目前可用的儲存以及它們的目標。
表 1. Dojo 中的可用儲存
| Dojo 儲存 |
用途 |
| dojo.data.ItemFileReadStore |
用於 JSON 資料的唯讀儲存。 |
| dojo.data.ItemFileWriteStore |
用於 JSON 資料的讀/寫儲存。 |
| dojox.data.CsvStore |
用於逗號分隔變數 (CSV) 格式資料的唯讀儲存。 |
| dojox.data.OpmlStore |
用於大綱處理標記語言(Outline Processor Markup Language,OPML)的唯讀儲存。 |
| dojox.data.HtmlTableStore |
用於 HTML 格式表格中所儲存資料的唯讀儲存 |
| dojox.data.XmlStore |
用於基本 XML 資料的讀/寫儲存 |
| dojox.data.FlickrStore |
針對 flickr.com 上的查詢的讀取儲存,是 Web 服務資料存放區的一個出色樣本 |
| dojox.data.FlickrRestStore |
針對 flickr.com 上的查詢的讀取儲存,是 Web 服務資料存放區的一個出色樣本。這是 FlickrStore 的一個更進階的版本。 |
| dojox.data.QueryReadStore |
類似於 ItemFileReadStore,是用於 JSON 資料的唯讀儲存,但會在收到每個請求時查詢服務器。 |
| dojox.data.AtomReadStore |
用於 Atom XML 文檔的讀取儲存。 |
自訂 Dojo 資料存放區
您也可以使用 Dojo.data API 編寫自訂資料存放區,資料訪問應該劃分為幾個部分,而且資料存放區應該使用合適的 API 實現每一部分。
dojo.data.api.Read 支援讀取資料項目和這些資料項目的屬性。這也包括搜尋、排序和過濾資料項目。
dojo.data.api.Write 支援建立、刪除和更新資料項目和這些資料項目的屬性。不是所有的後端服務都支援修改資料項目。實際上,大部分公用服務,比如 Flikr、Delicious 和 GoogleMaps,都主要是基於讀取的資料提供者。
dojo.data.api.Identity 支援基於項目的唯一識別碼(如果它有)來定位和尋找項目。不是所有的資料格式都具有可用於尋找資料項目的唯一識別碼。
dojo.data.api.Notification 支援通知監聽程式發生在儲存中的資料項目上的變更事件。一個項的基本變更事件包括建立、刪除和更新。這些變更對於會定期輪詢後端服務以進行資料重新整理的資料存放區尤其有用。
視圖
在 MVC 設計模式中,視圖從模型檢索應用程式資料並將其呈現給使用者。表格提供了許多函數來簡化對呈現的更改。在以下幾節中,我將展示一些典型用法,從視圖角度示範強大的表格功能。.
使用標記進行表格版面配置定義
總體而言,表格可以在 HTML 標籤中以聲明方式定義,也可以在 JavaScript 中以編程方式定義。清單 7 給出了一個使用標記的進階結構定義,它會產生 2 所示的顯示外觀。
清單 7. 使用標記定義布局的 JavaScript 代碼
<table id="grid" jsId="grid" dojoType="dojox.grid.DataGrid" store="myStore"
rowSelector="10px">
<thead>
<tr>
<th field="id" width="10px">ID</th>
<th field="name">Name</th>
<th field="manager" with="50px">Is manager</th>
<th field="sex" width="50px">Sex</th>
<th field="age" width="50px">Age</th>
<th field="date" width="100px">On Board date</th>
</tr>
<tr>
<th field="annualLeaveTotal" colspan="3">
Total annual leave days
</th>
<th field="annualLeaveTaken" colspan="3">
Annual leave days already taken
</th>
</tr>
</thead>
</table>
圖 2. 使用標記定義布局的表格
以編程方式定義表格版面配置
表的結構也可以以編程方式設定。structure 屬性可以指定一個對象來定義儲存格結構。
清單 8. 以編程方式定義布局的 JavaScript 代碼
var layout = [{
name: 'ID',
field: 'id',
width: '10px'
}, {
name: 'Name',
field: 'name',
width: '50px'
}, {
name: 'Is manager',
field:'manager',
width:'100px'
}, {
name: 'Sex',
field: 'sex',
width: '50px'
}, {
name: 'Age',
field: 'age',
width: '50px'
},{
name: 'On Board date',
field: 'date',
width: '100px'
}, {
name: 'Total annual leave days',
field: 'annualLeaveTotal',
width: '100px'
}, {
name: 'Annual leave days already taken',
field: 'annualLeaveTaken',
width: '100px'
}];
var grid = new dojox.grid.DataGrid({
id: 'grid',
store: myStore,
structure: layout
}, dojo.byId('grid'));
鎖定列,禁用橫向滾動
可以鎖定 一組列,阻止它們橫向滾動,而允許其他列繼續滾動。要實現此功能,您可以使用兩種結構並將一種結構的 noscroll 屬性設定為 true。
在清單 9 所示的樣本中,聲明了兩種結構。針對 ID 和 Name 列的結構的 noscroll 屬性設定為 true。然後使用一個數組將這兩種結構組合到一個布局結構中。
清單 9 .鎖定 ID 和 Name 列的 JavaScript 代碼
var fixlayout = {
noscroll: true,
cells: [{
name: 'ID',
field: 'id',
width: '10px'
}, {
name: 'Name',
field: 'name',
width: '50px'
}]
};
var mainlayout = {
onBeforeRow: beforerow,
onAfterRow: afterrow,
cells: [{
name: 'Is manager',
field: 'manager',
width: '200px'
}, {
name: 'Sex',
field: 'sex',
width: '50px'
}, {
name: 'Age',
field: 'age',
width: '50px'
}, {
name: 'On Board date',
field: 'date',
width: '100px',
}, {
name: 'Total annual leave days',
field: 'annualLeaveTotal',
width: '100px'
}, {
name: 'Annual leave days already taken',
field: 'annualLeaveTaken',
width: '100px'
}]
};
var layout = [fixlayout, mainlayout];
從圖 3 可以看出 ID 和 Name 列已被鎖定,但剩餘的列仍然可以橫向滾動。
圖 3. 具有固定列的表格
包含多行資料的行
表格支援單個邏輯行包含多行資料。這可以通過將 colSpan 屬性添加到布局定義中來實現,如清單 10 所示。
清單 10. 定義包含多行資料的行的 JavaScript 代碼
var layout = [[{
name: 'ID',
field: 'id',
width: '10px'
}, {
name: 'Name',
field: 'name',
width: '50px'
}, {
name: 'Is manager',
field:'manager',
width:'100px'
}, {
name: 'Sex',
field: 'sex',
width: '50px'
}, {
name: 'Age',
field: 'age',
width: '50px'
},{
name: 'On Board date',
field: 'date',
width: '100px'
}], [ {
name: 'Total annual leave days',
field: 'annualLeaveTotal',
colSpan: '2'
}, {
name: 'Annual leave days already taken',
field: 'annualLeaveTaken',
colSpan: '2'
}]];
名為 “Total annual leave days” 和 “Annual leave days already taken” 的列與其他列的資料位元於同一行
圖 4. 具有多行的表格
表格式資料格式
可以使用一種表格格式函數來更改資料存放區中的資料的呈現方式。這是 MVC 的一種核心概念。它可以定義一種符合使用者當地習慣的資料格式,比如日期,甚至可以構造 HTML 元件,比如複選框。清單 11 給出了一個樣本。
清單 11. 格式化表格式資料的 JavaScript 代碼
var dateFormatter = function(data, rowIndex){
return dojo.date.locale.format(new Date(data), {
datePattern: "dd MMM yyyy",
selector: "date",
locale: "en"
});
};
var managerFormatter = function(data, rowIndex){
if (data) {
return "<input type='checkbox' checked />";
}
else {
return "<input type='checkbox' />";
}
};
var layout = [{
name: 'ID',
field: 'id',
width: '10px'
}, {
name: 'Name',
field: 'name',
width: '50px'
}, {
name: 'Is manager',
field: 'manager',
formatter: managerFormatter,
width: '100px'
}, {
name: 'Sex',
field: 'sex',
width: '50px'
}, {
name: 'Age',
field: 'age',
width: '50px'
}, {
name: 'On Board date',
field: 'date',
width: '100px',
formatter: dateFormatter
}, {
name: 'Total annual leave days',
field: 'annualLeaveTotal',
width: '100px'
}, {
name: 'Annual leave days already taken',
field: 'annualLeaveTaken',
width: '100px'
}];
圖 5. 表格式資料格式
使用 get interface
您可以使用 get interface 在資料存放區之外定義其他列來動態檢索值。在上面的例子中,我擁有 “Total annual leave days” 和 “Annual leave days already taken” 列。如果您想知道還有多少天年假(可以根據現有的兩列計算得出),可以使用 get interface 動態檢索它。
我添加了一個名為 “Annual leave days left” 的新列,它的值為 “Total annual leave days” 減 “Annual leave days already taken” 的差,如清單 12 所示。
清單 12. 使用 get interface 的 JavaScript 代碼
function getLeftDays(rowIndex, item){
if (item != null) {
return item.annualLeaveTotal - item.annualLeaveTaken;
}
}
var layout = [{
name: 'ID',
field: 'id',
width: '10px'
}, {
name: 'Name',
field: 'name',
width: '50px'
}, {
name: 'Is manager',
field: 'manager',
formatter: managerFormatter,
width: '100px'
}, {
name: 'Sex',
field: 'sex',
width: '50px'
}, {
name: 'Age',
field: 'age',
width: '50px'
}, {
name: 'On Board date',
field: 'date',
width: '100px',
formatter: dateFormatter
}, {
name: 'Total annual leave days',
field: 'annualLeaveTotal',
width: '100px'
}, {
name: 'Annual leave days already taken',
field: 'annualLeaveTaken',
width: '100px'
}, {
name: 'Annual leave days left',
get: getLeftDays,
width: '100px'
}];
圖 6. 使用 get interface
控制器
在 MVC 設計模式中,控制器處理和響應事件(通常為使用者操作),並且可以間接調用模型上的變更。Dojo Grid 中的控制器具有非常強大的功能,它提供了許多方法來自訂表格格行為,例如如何處理事件、如何排序資料、如何過濾資料等。
在以下各節中,我將展示如何在 Dojo Grid 中使用和自訂控制器。
事件處理
Dojo Grid 具有一種強大的事件處理機制,它根據不同的表格元素和事件類型來提供事件調用介面。例如,它可以在一行或儲存格上響應 click 事件,它也可以響應 mouseover 事件。所以,在自訂這些事件處理方式來執行特定操作時,它非常有用。
我以 onCellClick 為例示範一下如何在 Dojo Grid 上添加自己的處理常式。在本例中,我自訂該方法來顯示儲存格的值以及行和列的索引。(參見清單 13)。
清單 13. 自訂表格格的 onCellClick 事件處理常式的 Javascript 代碼
<script>
var showDetail = function(e){
var value = e.cellNode.firstChild.data;
alert('value:' + value + " column:" + e.cellIndex + " row:" + e.rowIndex);
}
dojo.addOnLoad(function(){
dojo.connect(grid, "onCellClick", showDetail);
}
</script>
首先,您需要定義事件處理常式 showDetail 來顯示儲存格詳細資料(值、列索引和行索引)。接下來,您需要使用 dojo.connect 來將自訂處理常式串連到 onCellClick 事件。您必須在 dojo.addOnLoad 中執行同樣的操作,因為該方法可確保所有 Dojo 小組件都已完成初始化並可供使用。
當使用者單擊表格的儲存格時,應用程式將顯示一個警報視窗。圖 7 顯示了得到的結果。
圖 7. 表格的自訂事件處理常式
自訂排序
Dojo Grid 提供了根據列的資料類型來排序的基本排序功能。例如,在我的樣本中,ID 列按照數字順序進行排序,名稱列按照字母順序進行排序。
Dojo Grid 中的排序功能也可以自訂。您可以定義自訂排序行為或阻止使用者對某些列排序。如果您不希望使用者對某些列排序,可以使用 Dojo Grid 的 canSort 屬性來指定哪些列可以排序。
清單 14 給出了禁用 ID 列的排序功能的 JavaScript 代碼。
清單 14. 指定哪些列可以排序的 Javascript 代碼
<script>
dojo.addOnLoad(function(){
grid.canSort = function(index) {
if(index == 1) return false;
return true;
};
}
</script>
參數索引為表格的列索引,從 1 開始。如果 canSort 函數返回 false,則表示禁用了列排序功能。
除了指定哪些列可以排序之外,您還可以指定如何對列進行排序。我在樣本中使用 Name 列為例。圖 8 顯示了 Dojo Grid 的預設排序行為。
圖 8. Dojo Grid 的預設排序行為
我按降序對名稱列進行排序。請注意最後 3 行:順序為 Victor、Wang 和 vicky。表格的預設排序方式是區分大小寫,並使用 ASCII 碼順序進行排序。所以,小寫字母將排在大寫字母之後。但是,這種行為不符合軟體全球化標準。在這種情況下,您需要自訂排序函數來支援全球化的排序方式。
看一下清單 5 中的 JavaScript 代碼,查看如何自訂 Dojo Grid 的排序函數。
清單 15. 自訂 Dojo Grid 的排序函數
<script>
dojo.addOnLoad(function(){
myStore.comparatorMap = {};
myStore.comparatorMap["name"] = function (a, b) {
return a.localeCompare(b);
}
}
</script>
資料存放區對象中有一個名為 comparatorMap 的欄位,有了它就可以更改排序行為。在本樣本中,我為名稱列定義了一個比較方法,使用 localeCompare 來支援全球化排序。圖 9 顯示了經過排序自訂之後的結果。
圖 9. 自訂的 Dojo Grid 排序行為
過濾器
Dojo Grid 提供一種非常方便的方式來在用戶端過濾資料。您可以定義一列的過濾條件。清單 16 展示了如何過濾表格,僅顯示以字母 A 開頭的名稱。
清單 16. 過濾名稱列
<div dojoType="dijit.form.Button">filter name
<script type="dojo/method" event="onClick" args="evt">
// Filter the name from the data store:
grid.filter({name: "A*"});
</script>
</div>
單擊 filter name 按鈕之後,過濾結果將 10 所示。
圖 10. 經過過濾的表格
結束語
本文通過 MVC 設計模式介紹了 Dojo Grid 的主要功能。通常,可以採用多種方式實現一項功能。例如,要在表格中顯示日期,既可以使用字串來在資料存放區中表示日期,也可以聲明一個 long 並在最終顯示中為其設定正確的格式。咋看起來,第一種選擇似乎更簡單。但是,如果需要使表格全球化,後一種選擇更好。希望您在自己的 Dojo Grid 項目中使用 MVC 設計模式。您將會進一步提高代碼的健壯性和可重用性。