dojo grid
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},
…..
}
在此示例中:
- 每个项目都有八个属性。
-
id
属性是一个不能重复的标识符。
数据存储可以通过两种方式构建:声明为标记标签或以编程方式构建。
使用标记标签构建数据存储
要使用标记标签构建数据存储,首先需要JSON文件,该文件将存储所有组织的数据(请参见清单2)。 在本文中,我使用data.json。 然后,您可以将清单2中的标记写到HTML文件中。
清单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文件中获取数据,然后将网格存储设置为“ myStore”。 生成的网格的示例如图1所示。
图1.构造后的简单网格
使用标记标签构建网格存储非常简单且易于使用。 但是,如果数据来自服务器并且是动态组织的,则需要以编程方式构建网格及其存储。
以编程方式构建数据存储
要与服务器端响应一起动态地构建和更改网格的存储,您必须:
- 使用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.创建并设置网格存储
dijit.byId("grid").store = new dojo.data.ItemFileReadStore({
data: this.generateStoreData(itemList)
});
所有这些步骤为您提供了如图1所示的相同网格。
查询数据存储
Dojo Grid通常将整个数据源存储在其数据模型中。 但是,随着数据大小的增长,这可能会导致一些性能问题。 实际上,当Dojo网格存储中的项目超过一定数量时,并且如果每个项目都有许多属性,则网格操作(如排序,搜索和渲染)的性能将急剧下降。
但是,有一些方法可以提高性能。 您可以编写代码以使服务器将有限的数据发送到浏览器并将它们构造到网格数据存储中,或者您可以简单地使用或扩展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.data.ItemFileReadStore | JSON数据的只读存储。 |
dojo.data.ItemFileWriteStore | 读/写JSON数据存储。 |
dojox.data.CsvStore | 只读存储的逗号分隔变量(CSV)格式的数据。 |
dojox.data.OpmlStore | 大纲处理器标记语言(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'
}]];
名为“年假总天数”和“已休年假天数”的列与其他列在同一行中
图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界面
您可以使用get interface
在数据存储之外定义其他列以动态检索值。 在上面的示例中,我有两个“总年假天数”和“已采取的年假天数”列。 如果我想知道还剩下多少年假,可以根据现有的两列来计算,我可以使用get interface
动态地获取它。
我添加了一个名为“剩余年假天数”的新列,该列的值是通过从“总年假天数”值中减去“已采取的年假天数”值来计算的,如清单12所示。
清单12.使用get接口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接口
控制者
在MVC设计模式中,控制器处理并响应事件(通常是用户操作),并且可以间接调用模型上的更改。 Dojo Grid中的控制器非常强大,它提供了许多自定义网格行为的方法。 例如,如何处理事件,如何对数据进行排序,如何对数据进行过滤等等。
在以下各节中,我将说明如何在Dojo Grid中使用和自定义控制器。
事件句柄
Dojo Grid具有强大的事件处理机制,可根据不同的Grid元素和事件类型提供事件调用界面。 例如,它可以响应行或单元格上的click
事件,并且可以响应mouseover
事件。 因此,自定义这些事件句柄以执行特定任务对您非常有用。
我以onCellClick
为例,展示了如何在Dojo Grid上添加自己的处理程序。 在这种情况下,我将自定义方法以显示单元格的值以及列和行的索引。 (请参见清单13。)
清单13.定制Grid的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. Grid的定制事件处理程序
定制排序
Dojo Grid提供了基于列数据类型的基本排序功能。 例如,在我的示例中,ID列按数字顺序排序,名称列按字母顺序排序。
也可以自定义Dojo Grid中的排序功能。 您可以定义自定义的排序行为或阻止用户对某些列进行排序。 如果您不希望用户对某些列进行排序,请使用Dojo Grid的canSort
属性指定可以对哪些列进行排序。
清单14显示了禁用ID列上的sort函数JavaScript代码。
清单14.用于指定可以对哪些列进行排序JavaScript代码
<script>
dojo.addOnLoad(function(){
grid.canSort = function(index) {
if(index == 1) return false;
return true;
};
}
</script>
参数索引是网格的列索引,从1开始。如果canSort
函数返回false,则禁用列排序功能。
除了指定可以对哪些列进行排序之外,您还可以指定如何对列进行排序。 我在示例中使用“名称”列。 图8显示了Dojo Grid的默认排序行为。
图8. Dojo Grid的默认排序行为
我按降序对名称列进行排序。 请注意最后三行:顺序为Victor,Wang和vicky。 网格默认排序区分大小写,并使用ASCII码顺序排序。 因此,小写字母将放置在大写字母之后。 但是,此行为不符合软件全球化标准。 在这种情况下,您需要自定义排序功能以支持全局排序。
看一下清单15中JavaScript代码,看看如何定制Dojo Grid的sort函数。
清单15.自定义Dojo Grid的排序功能
<script>
dojo.addOnLoad(function(){
myStore.comparatorMap = {};
myStore.comparatorMap["name"] = function (a, b) {
return a.localeCompare(b);
}
}
</script>
数据存储对象中有一个称为comparatorMap
字段,因此您可以更改排序行为。 在此示例中,我为name列定义了比较器方法,并使用localeCompare
支持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>
单击过滤器名称按钮后,将显示过滤器结果,如图10所示。
图10.过滤后的网格
结论
本文介绍了使用MVC设计模式的Dojo Grid功能。 通常,可以以各种方式来实现功能。 例如,要在网格中显示日期,可以使用字符串表示数据存储中的日期,也可以声明一个long并在最终显示中为其设置正确的格式。 首先,首选似乎更容易。 但是,如果必须全球化网格,则后一种选择更好。 我鼓励您在Dojo Grid项目中使用MVC设计模式。 您将获得更高级别的代码健壮性和重用性。
翻译自: https://www.ibm.com/developerworks/web/library/wa-aj-dojogrid/index.html
dojo grid