数据呈现是RIA应用中的一个重点应用,各种JavaScript框架也一般都提供了自己的Grid小部件用于呈现表格类数据。而TreeGrid作为一种特殊的Grid,顾名思义,更是兼具了Tree多层级结构以及Grid的多数据项复杂数据展示的优点,是一种很好的处理复杂多级数据的控件。然而,无论对于Tree或者TreeGrid,通常由于实现方面的种种限制,对数据的延迟加载只能是针对层级结构而言的,即在展开某一节点时即时请求该节点下的全部子节点。尽管这对于一般的应用场景来说基本可以满足需求,但在当次级节点下数据结构较复杂,节点繁多的情况下,则可能造成极其严重的性能问题。针对这一特定需求,Dojo从1.6开始推出了一个全新的控件——LazyTreeGrid。
作为Dojo DataGrid的扩展,LazyTreeGrid在整体的数据结构上采用的仍然是MVC结构。LazyTreeGrid的结构
图1. LazyTreeGrid结构模型
图1就是TreeGrid的一个基本架构模型,就整体结构而言,LazyTreeGrid与TreeGrid、DataGrid并没有太大区别。视图即为用户直接可见的部分,包括了Grid的表头、行、列、单元格及TreeGrid特有的节点展开按钮等直观内容,整个TreeGrid通过内容视图中的虚拟滚动条的滚动事件以及节点展开按钮Expando的展开或关闭事件的触发来获取数据并构建内容。
然而,为了满足针对次级节点的分页延迟加载及渲染功能,LazyTreeGrid则需要基于树状层级结构要求在Model和View部分进行相应的扩展。下面就基于LazyTreeGrid的数据模型及视图结构来对其设计思路及实现方式做一个简单介绍。
LazyTreeGrid的数据模型
正常状态的树状结构数据是层级嵌套模式的,如下例所示:
data = {
identifier: 'id',label: 'name',
items: [
{
id: 'AF',name: 'Africa',
children: [
{
id: 'EG', name: 'Egypt'
},
{
id: 'KE', name: 'Kenya',
children: [
{
id: 'Nairobi', name: 'Nairobi', type: 'city'
},
{
id: 'Mombasa', name: 'Mombasa',type: 'city'
}
]
},
...
]
},
...
]
}
与其他的可延迟加载设计的Tree类型应用的数据实现要求类似,为了达到延迟加载次级数据的目的,需要对父节点数据做压平处理,将其与子节点在结构上进行分离,这样才可以在进行数据的最初请求时只加载必须的父节点数据,仅在展开父节点时再延迟加载其下的次级数据。另外,在LazyTreeGrid中,对于某些子节点数目很少,不需要延迟加载的情况,这里也允许存在未被压平的数据节点,如下例所示:
data = {
identifier: 'id',label: 'name',
items: [
{
id: 'AF', name: 'Africa',children: 10
},
{
id: 'EG', name: 'Egypt',children: false
},
{
id: 'KE',name: 'Kenya',
children: [
{
id: 'Nairobi',name: 'Nairobi',type: 'city'
},
{
id: 'Mombasa', name: 'Mombasa',type: 'city'
}
]
},
...
]
}
LazyTreeGrid所要求的数据结构允许父节点将原有的嵌套数据替换为一个正整数数值或布尔值,用以代表其下的子节点数目或者是否有子节点(false,非正数或者没有相应属性都代表该节点没有子节点)。需要注意的是,LazyTreeGrid要求数据必须拥有一个唯一的主键id,这样才可以通过该id去服务器端请求相应数据条目的子数据。
在LazyTreeGrid数据模型(model)部分,除了DataGrid原有的DataStore(Dojo数据存储器)外,由于Dojo本身的DataStore对树形数据结构的API方面的支持不足,另外增加了一个TreeModel用于提供针对树形结构数据的特定支持。而为了能够满足分页加载次级数据的要求,LazyTreeGrid实现了一个特殊的TreeModel:dojox.grid.LazyTreeGridStoreModel,其主要功能就是建立一个后台数据获取协议,通过指定父节点与子节点序列来使服务器端正确返回相应的分页数据,在请求次级数据时,LazyTreeGrid将向后台服务端发送类似如下的一条请求:
http://localhost:8080/TreeGrid/FakeDataServlet?parentId=root1&start=0&count=25
在这里,parentId即为要请求数据的父节点id,而start和count分别代表了请求起始的子节点序列和请求的节点个数。
下面的代码给出了如何建立一个简单的LazyTreeGridStoreModel:
// programmatic
var treeModel = new dojox.grid.LazyTreeGridStoreModel({
store: queryReadStore,
serverStore: true
});
// declarative
<span data-dojo-type="dojox.grid.LazyTreeGridStoreModel"
data-dojo-props="store:queryReadStore, serverStore:true" >
</span>
建立一个LazyTreeGridStoreModel需要确定两个参数:store和serverStore,store用于指定获取数据的dojo DataStore;serverStore接收一个布尔值,用于确定是否数据由服务器端传递且满足数据条目是被压平存储、传输的(存储在客户端的数据没必要采用延迟加载模式)。对于次级数据量不大,不需要分页加载子数据的情况,用户也可以选择使用Dojo原有的dijit.tree.ForestStoreModel。
LazyTreeGrid的视图(View)
在Dojo1.6之前存在的dojo.grid.TreeGrid,采用的视图构建方式是认为所有的子节点都是最上级父节点的内容扩展,即在一行之内渲染出所有的展开的子节点结构,如下图所示:
图2. Dojox.grid.TreeGrid视图
在这个图例中,Grid每行最左侧的就是rowSelector——行选择按钮,根据rowSelector的分配情况,我们就可以清楚的看出其行结构是按第一级节点进行划分的。
尽管就整体视图结构来看这一做法并无不妥,但由于TreeGrid复用了DataGrid的按行结构进行分页的延迟加载与渲染机制,因此位于当前页的所有行的内容就都会被一次加载及渲染。那么当次级节点较多、较复杂的情况下,这种加载,尤其是渲染所带来的资源消耗以及响应时间就会变得非常突出和难以忍受了。
在LazyTreeGrid中,根据在数据模型中对数据进行的预处理,在保留层级信息的基础上对数据进行了分离处理,这样就可以在视图中将各条数据都作为一条独立数据行进行加载渲染。由图3中可以看出,每条数据都是单独的一行,因此,在复用了DataGrid的Virtual Scroller机制的前提下,即使数据中包含了很多的次级节点,也会忽略其层级结构,仅根据对Grid的分页配置进行划分,延迟加载与渲染数据条目,从而达到性能上的极大提升。
图3. LazyTreeGrid视图
LazyTreeGrid中的其他特性
在树状结构数据中,可能会存在不同级别的数据条目中的数据列不完全相同的情况,更常见的是上级数据为概要据,而次级数据则作为详细数据存在。因此在这一类情况下,用户有可能需要对不同级别数据定制出不同的表达格式。针对这种需求,LazyTreeGrid分别支持分级合并单元格设置和分级数据格式化设置。
图4. LazyTreeGrid实例
图4就是一个基于一个简单化的存储管理表格示例,最顶级数据是存储器群组,第二级和第三级数据分别是物理磁盘和虚拟磁盘。在这一示例中,就根据根级数据和次级数据的数据不一致性做了分级合并单元格和分级单元格格式化,使得整体视图更加清晰明了。
分级合并单元格需要在声明LazyTreeGrid时为其添加一个colSpans属性:
colSpans: {0: [ {start: 0, end: 1}, {start: 2, end: 3, primary: 2}], 1: […], 2:… }
colSpans属性接收一个JSON对象,该对象中的键0/1/2/…分别对应各个级别,其值则用于指定单元格合并的细节,start代表从第几个单元格开始合并,end指合并到第几个单元格(都是以0作为起始值),而primary则指示了合并后显示第几个单元格的内容,在没有指定的情况下,其默认值等于start的值。
而分级单元格格式化则继承了与DataGrid的单元格格式化相同的方式,即分别为各个列设定formatter函数,不同的是,所提供的formatter函数的第三个参数为该行数据所在的级别,下例给出了一个简单的formatter函数:
var fmtByLevel = function(value, idx, level) {
return level == 0 ? "root" : level == 1 ? "2nd" : "3rd";
};
LazyTreeGrid的使用
下面,我们就基于图4中的示例,简述如何构建一个LazyTreeGrid。
简单而言,由于LazyTreeGrid继承于DataGrid,所以其基本创建过程与DataGrid基本一致,主要的区别就是前面提到过的需要在Grid和DataStore之间加入一个TreeModel。下面就是以JS编程方式创建这一LazyTreeGrid的前台代码示例。
首先需要对LazyTreeGrid的视图结构进行定义:
// LazyTreeGrid结构定义
var layout = [{
name: "Name",
field: "name",
width: "150px",
formatter: fmtName
}, {
name: "Status",
field: "status",
width: "40px",
formatter: fmtStatus
}, {
name: "Capacity",
field: "capacity",
width: "80px",
formatter: fmtCapacity
}, {
name: "UID",
field: "uid",
width: "240px"
}];
Layout主要定义了Grid的列名、列宽度、相应的取值数据项等,另外我们也可以看到,针对前三个列分别定义了三个formatter函数,用于定义分级的单元格格式化。其中的fmtCapacity函数采用了对第一级数据返回一个progressbar的小部件:
// progress bar formatter
var fmtCapacity = function (value, idx, level) {
return level == 0
new dijit.ProgressBar({
progress: value * 100,
maximum: 100,
report: function (percent) {
return (percent * 100).toFixed(2) + "% Storage Online";
}
}): value + "GB";
};
DataStore方面采用的是dojox.data.QueryReadStore,通过同域中的一个servlet获取数据,并基于这个DataStore创建了一个LazyTreeGridStoreModel来连接到LazyTreeGrid。如下段代码所示:
// 创建DataStore
var queryReadStore = new dojox.data.QueryReadStore({
id: "queryReadStore",
url: "FakeDataServlet"
});
// 创建TreeModel
var treeModel = new dojox.grid.LazyTreeGridStoreModel({
id: "treeModel",
serverStore: true,
store: queryReadStore
});
最后,我们需要在页面中放入一个id为'gridContainer'的div作为LazyTreeGrid的容器元素,并在页面加载完成后对LazyTreeGrid的创建及初始化操作:
// 创建LazyTreeGrid
var grid;
dojo.addOnLoad(function () {
grid = new dojox.grid.LazyTreeGrid({
id: "grid",
rowSelector: true,
treeModel: treeModel,
structure: layout,
colSpans: {
0: [{
start: 0,
end: 1
}, & nbsp; {
start: 2,
end: 3,
primary: 2
}]
}
});
dojo.byId('gridContainer').appendChild(grid.domNode);
grid.startup();
});
至此,我们已经完成了LazyTreeGrid的页面创建工作,接下来的工作就是创建合适的后台代码来正确响应LazyTreeGrid的数据请求。
小结
作为Dojo1.6新引入的一个Widget,尽管LazyTreeGrid仍有很多亟待解决的缺陷或问题,如尚不支持初始化及运行时的Expand/Collapse All的功能、可调用API较少等,但它作为一种呈现复杂多层级数据的RIA应用小部件,其对于延迟加载与延迟渲染方面所提出的解决方案还是具有一定的突破性的,具有这方面应用要求的用户不妨一试。
本文已经首发于InfoQ中文站,版权所有,原文为《Dojo中的LazyTreeGrid控件》,如需转载,请务必附带本声明,谢谢。
InfoQ中文站是一个面向中高端技术人员的在线独立社区,为Java、.NET、Ruby、SOA、敏捷、架构等领域提供及时而有深度的资讯、高端技术大会如QCon、线下技术交流活动QClub、免费迷你书下载如《架构师》等。
分享到:
相关推荐
### Dojo控件的使用和入门心得 #### Dojo简介及其优势 Dojo是一个功能强大的JavaScript框架,专门设计用于简化富互联网应用(RIA)的开发。作为一种DHTML Toolkit,Dojo封装了大量的常用功能,旨在提高前端开发...
手册中列举了一些常用的Dojo包,如dojo.io用于不同类型的IO传输,dojo.dnd提供拖放功能的API,dojo.string提供了字符串处理方法,dojo.date帮助解析和操作日期,dojo.event处理事件驱动和AOP开发,dojo.back管理撤销...
使用CDN上的DOjo非常快捷,用户只需添加一个script标签到HTML文件中就可以使用DOjo。 DOjo的基本使用 DOjo是一个JavaScript库,提供了许多有用的功能,如DOM操作、事件处理、AJAX请求等。DOjo的使用需要一个网络...
DOJO的核心目标是解决长期以来在开发DHTML应用过程中遇到的历史问题,特别是跨浏览器兼容性问题。 #### 二、DOJO的功能特点 ##### 1. 动态网页支持 - **增强网页动态能力**:利用DOJO提供的组件,可以显著提高Web...
DOJO学习笔记(七)-日期控件DropdownDatePicker和DatePicker DOJO常用的验证函数 Dojo with Adobe AIR Dojo 工具包教程 Dojo 快速安装 Dojo和JSON建立无限级AJAX动态加载的功能模块树 Dojo学习笔记( 模块与...
此外,Dojo还提供了一套强大的DOM操作API,如dojo.create、dojo.destroy和dojo.style,使得在JavaScript中操作DOM变得简单易行。 在数据交互方面,Dojo的dojo.xhr系列函数(如dojo.xhrGet、xhrPost)实现了与服务器...
DOJO学习笔记(七)-日期控件DropdownDatePicker和DatePicker DOJO常用的验证函数 Dojo with Adobe AIR Dojo 工具包教程 Dojo 快速安装 Dojo和JSON建立无限级AJAX动态加载的功能模块树 Dojo学习笔记( 模块与...
Dijit是Dojo的UI组件库,包含各种可复用的、样式化的用户界面元素,如按钮、表单控件、布局组件等。Dijit组件遵循WAI-ARIA无障碍标准,确保了良好的可用性。 ### 5. Dojo DojoX DojoX是Dojo的扩展库,包含更专业或...
DOJO中文手册是针对JavaScript库Dojo的详细指南,源自中国的本地化版本,旨在帮助开发者理解和使用这个强大的工具包。Dojo是一个开源的DHTML工具集,由nWidgets、Burstlib和f(m)等多个项目的合并发展而来,因此被...
中文入门手册可能会介绍如何使用Dojo的Dijit框架创建可复用的UI组件,Dijit提供了一系列预定义的UI控件,如按钮、表单、对话框等,它们具有良好的跨浏览器兼容性和可定制性。 此外,手册可能还会涉及Dojo的Store...
3. **dojo/ready**:这个模块用于确保DOM加载完成后再执行指定的函数,它是Dojo中的一个实用工具,常用于页面初始化。 4. **dojo/store**:这是一个数据存储抽象层,提供了一种统一的方式来访问和操作数据,无论...
5. **Dojo Widget系统 (dijit)**: dijit是Dojo的UI组件库,包含了各种可复用的、可配置的Web部件,如按钮、表单控件、对话框等。这些部件是模块化的,且遵循WAI-ARIA无障碍标准,以确保良好的用户体验。 6. **...
在这个场景中,"lazyTreeGrid" 是Dojo框架提供的一种高效处理大量数据的组件,它允许我们在用户展开树节点时按需加载子节点数据,而不是一次性加载所有数据,从而显著提升页面加载速度和用户体验。 **lazyTreeGrid...
DOJO学习笔记(七)-日期控件DropdownDatePicker和DatePicker DOJO常用的验证函数 Dojo with Adobe AIR Dojo 工具包教程 Dojo 快速安装 Dojo和JSON建立无限级AJAX动态加载的功能模块树 Dojo学习笔记( 模块与...
在Dojo框架中,有几个核心概念和关键组件是学习的重点: 1. **模块系统(AMD,Asynchronous Module Definition)**:Dojo是AMD规范的先驱,它允许异步加载和组织代码,使得大型项目的结构更加清晰。通过`require`和...
Dojo 是一个基于 JavaScript 的开源工具包,旨在简化DHTML应用程序的开发,尤其注重解决跨浏览器的兼容性问题。...通过其模块化设计和对跨浏览器兼容性的关注,Dojo 成为了现代Web开发中不可或缺的一部分。
dojo中文文档dojo中文文档
【dojoMap Demo】是一个展示如何在Dojo框架中集成地图数据图表的应用示例。这个Demo是基于Dojo 1.5版本构建的,它演示了如何利用Dojo库中的功能来创建交互式地图和图表,将地理信息与数据可视化相结合。在本篇中,...
6. **dijit组件**:Dojo的dijit模块提供了一系列用户界面组件,如按钮、表单控件、布局容器等,这些组件具有丰富的主题和无障碍性支持。 7. **dojo/parser和声明式编程**:dojo/parser负责解析页面上的声明式标记,...