树,过去用于在父-子关系中导航分层数据,能够帮助您轻松地理解长的数据和数据关系清单。Dojo 支持创建树,并且 Dojo 中的树遵从 Model-View-Controller(MVC)设计模式。在数字的树中,角色由以下的组件扮演:
- 控制器(dojox.data 存储)
- 该存储负责存储所有非关系型数据库格式的数据。
- 模型(dijit.tree ForestStoreModel/TreeStoreModel)
- 根据用户选择的模型,格式化存储在层次顺序中的数据。
- 视图(dijit.Tree)
- 显示由模型对象创建的数据的树。
使用 Dojo 树中的 MVC,树的创建由控制器开始,然后是模型,最后是视图,如图 1 所示。
图 1. 遵从 MVC 设计模型的 Dojo 树
您可以使用各种 Dojo 数据存储在 Dojo 中创建树。在本文中,学习使用基于 REST API 的 Dojo 存储 JsonRestStore 创建一个延迟加载的 Dojo 树。
延迟加载是一种仅在需要时,从用户数据库获取数据的技术。服务器只获取那些由用户展开的节点,这可以优化树的性能。树的延迟加载根据树中存储的数据量,可能导致廉价的,或者昂贵的操作。
在少量的数据上应用延迟加载会导致对服务器的多次请求,而缓慢的响应可能会导致用户的沮丧。但是,在一个有大量数据集的生产环境中,延迟加载往往为您提供优势。
在深入了解 Dojo 树创建过程之前,下一部分包含了一些创建延迟加载树的关键概念。
一个 Dojo 树中有几个重要的构建模块:
-
JsonRestStore引用功能 deferItemLoadingUntilExpand- JSON 数据
JSON 引用是由 JsonRestStore 提供的属性,您使用它从一个对象获取另一个对象的引用(无需包括真实的对象)。从一个对象引用另一个是由 JsonRestStore 的 $ref 属性提供的。
父-子关系可以在运行时,使用父级中 $ref 属性的值解决,这个值还是子节点的 id 值。父节点的 JSON 对象可能类似以下例子。
{
"id": "node1",
"name": "Subjects",
$ref: "child1"
}
|
子级 JSON 对象类似以下例子。
{
"id": "child1",
"name": "Math",
$ref: "author1"
}
|
在第一段代码例子中,父节点有一个 "child1" 的 $ref 属性值,这就意味着它引用子节点的 id 值。这个值可以在父节点被展开时才进行解析(本文稍后显示)。
在第二段代码例子中,id
“child1” 要完全匹配父级 $ref 值。您可以使层次结构继续,类似第二个例子中的 $ref:"author1"。
deferItemLoadingUntilExpand 属性
Dojo Toolkit 1.4 推出了一个新属性,deferItemLoadingUntilExpand,用于 Dojo 树模型。Dojo Toolkit 的较早版本缺少此功能,当任意子节点被扩展时,所有树节点都被重新获取,并加载到 Dojo 数据库。但是,使用 deferItemLoadingUntilExpand 属性,如果 deferItemLoadingUntilExpand 被设置为 true,它就只能获取被请求节点的子级,并在数据存储中进行更新。这会帮助增加延迟加载树的效率。
JsonRestStore 有一个 JSON 格式数据的预定义模式。在开始创建树之前,您需要具备对 JSON 的基本理解。本文使用树的 JSONObject,JSONArray 和 JsonString,所以例子可以获得正确格式的数据。本文中的例子从 Java™ 类中获取 JSON 数据,并且需要 JSON 数据作为由 JsonRestStore 处理的返回字符串。
在这一部分,您可以使用 JsonRestStore 创建一个延迟加载的树。例子中创建了一个小说书店的树。这个树中将会包含书籍和作者的信息,书籍和作者之间有分层结构。您可以使用延迟加载的 Dojo 树创建这个分层结构,使应用程序更高效。
Dojo 树使用 MVC 模型,这个模型有两个形式:TreeStoreModel 和 ForestStoreModel。例子使用 ForestStoreModel 来构建一个树。首先,您需要声明和定义应用程序的 JsonRestStore。使用 REST 服务创建一个 JsonRestStore,或者直接映射一个 URL 到目标参数。
例子声明了一个服务,它将基于 Dojo 的 Asynchronous JavaScript 和 XML(Ajax)调用放置到一个负责从数据库获取数据,并返回 JSON 格式字符串的 Java 类中。从 Java 类检索后,数据更新为服务参数。
从生成客户端代码入手,如清单 1 中所示的 JsonRestStoreTree.jsp 代码。
清单 1. 创建 REST 服务和 JsonRestStore
var booksService = function(query, queryOptions)
{
return dojo.xhrGet({url:"
/BookStoreTree/com/tree/actions/JsonRestStoreTreeAction.action",
handleAs:'json',content:{query:query, queryOptions:queryOptions}});
};
var booksDataStore = new
dojox.data.JsonRestStore({target:'booksData',service: booksService,
labelAttribute:"name"});
|
或者,可以通过制定存储目标直接创建存储,如清单 2。
清单 2. 使用目标参数创建存储
var booksDataStore = new dojox.data.JsonRestStore({target:
"/BookStoreTree/com/tree/actions/JsonRestStoreTreeAction.action ",
labelAttribute:"name"});
|
现在已经创建了存储,那您就需要使用 ForestStoreModel 创建一个模型。例子中编程创建了一个模型,如清单 3。代码将 deferItemLoadingUntilExpand 值设置为 true。
清单 3. 创建模型
var treeModel = new dijit.tree.ForestStoreModel({
store: booksDataStore,
deferItemLoadingUntilExpand: true,
rootLabel: "Subjects",
childrenAttrs: ["children"]
});
|
存储和模型已经完成,现在继续创建 Dojo,如清单 4。
清单 4. 创建 Dojo 树
<div dojoType="dijit.Tree" model="booksDataStore "
persist="false" id="booksDataTree">
|
与创建树的客户端代码一样,开始编写 Java 代码来获得 JSON 格式的书籍的初始数据。
清单 5 中的代码返回了数据的 JSON 格式。数据稍后存储在 booksDataStore。
清单 5. 获取初始树数据的 Java 代码
public String getSubjectList() throws IOException
{
//Code to query subjects list from DB
//call DB-side function to get the books object
Map<String,String> booksNameMap = new
HashMap<String, String>();
//ex. Map booksNameMap =
server.GetListOfSubjects();
//above code is specific to application
JSONObject subObj = new JSONObject();
JSONArray subArray = new JSONArray();
for(int i=0; i< booksNameMap.size(); i ++)
{
String subjectName = booksNameMap.get(i);
subObj.put("id", subjectName +"Id");
subObj.put("name", subjectName);
subObj.put("$ref","author"+ i);
subObj.put("children", true);
}
subArray.add(subObj);
jsonString = subArray.serialize(true);
setJsonString(jsonString);
return jsonString;
}
|
清单 5 中的代码在 JSONArray 中存储数据,这是加载树的初始数据所需要的。创建的树如图 2 所示。
图 2. 初始 Dojo 树
现在已经创建了树,您可以扩展获取被请求节点数据的节点了。您将会看到引用属性在扩展任意节点时起到多么重要的作用。
清单 6 中,扩展 id
"subjectNameId" 父级节点时,就会进行 getListOfAuthors() 方法调用。因为 "subjectNameId " 的 $ref 属性被设置为 "author1",author1 的节点数据被返回,而且自动更新为树的子节点。
清单 6. 获取子节点数据
public String getListOfAuthors() throws IOException
{
//Code to query authors list from DB
//call DB-side function to instantiate
authorsNameMap object
Map<String,String> authorsNameMap = new
HashMap<String, String>();
//ex. Map authorsNameMap =
server.GetListOfAuthors();
//above code is specific to application
JSONObject authObj = new JSONObject();
for(int i=0; i< authorsNameMap.size(); i ++)
{
authObj.put("id", "author"+i);
authObj.put("name",
authorsNameMap.get(i));
}
jsonString = authObj.serialize(true);
setJsonString(jsonString);
return jsonString;
}
|
尽管父级数据以 JSONArray 格式,而例子中以 JSONObject 格式发送子节点数据。这非常重要,因为子数据是以 JSONObject 格式被捕获。
已创建的树如图 3 所示。当 C Programming 节点被扩展时,就会有请求发送给服务器,只获取这个节点的数据。
图 3. 延迟加载的 Dojo 树
在删除,添加,或者更新节点时,您不更新树视图。反而,您需要更新连接到数据存储的树模型。最重要的是,如果想要修改树,您需要更新数据存储。
在书店的例子中,存储数据包含各种书籍的信息,每本书都由 "id" 值惟一地识别。您可以使用 "id" 值获取节点,并从存储中删除节点。清单 7 显示了从树中删除节点的代码段。
清单 7. 从 Dojo 树中删除节点
function deleteNodeFromTree()
{
//delete from the bookstore//
booksDataStore.fetchItemByIdentity({
identity: 'AlgorithmsId',
onItem: dojo.hitch(booksDataStore,
function(item) {
booksDataStore.deleteItem(item);
booksDataStore.save({
onComplete: function() {
alert("Save Complete");
}
});
})
});
}
|
清单 7 中的代码通过节点的标识(在该例子中为 'AlgorithmsId')从存储中获取节点。获取所需节点后,调用 JsonRestStore 的 deleteItem() 方法,这可以从数据存储中删除 'AlgorithmsId' 数据。删除节点后,Dojo 树如图 4 所示。
图 4. 算法节点被删除
JsonRestStore 有 newItem(item, {parent : parentItem, attribute : children}) 功能,用于在存储中添加新项目,您还可以使用它添加一个新节点到树中。该方法的调用类似例子中删除节点时的 deleteItem() 调用。在书店中,添加 "A.K.Das" 作为 Data Structures 节点的子级。
清单 8. 在 Dojo 树中添加节点
function addNodeToTree()
{
//add data to the bookstore//
booksDataStore.fetchItemByIdentity({
identity: 'AlgorithmsId',
onItem: dojo.hitch(booksDataStore,
function(item) {
booksDataStore.newItem(
name: 'A.K.Das',
ref: $'AlgorithmsId',
item);
booksDataStore.save({
onComplete: function() {
alert("Add node
Complete");
}
});
})
});
}
|
清单 8 中的代码获取您想要添加新子级的项目。或者,换句话来说,新节点成为您正在从存储中获取的节点的子节点。父节点是 'AlgorithmsId',即书名,例子中添加一个 'A.K.Das' 作为作者。图 5 显示了添加新节点之后的树。
图 5. 子节点被添加到 Algorithms 节点
要更新树的节点,您需要更新位于数据存储的数据。再一次,例子调用数据存储的 fetchItemByIdentity 方法,使用 JsonRestStore 的 setValue(item, attribute, value) 方法来更新目标节点的数据。
清单 9 显示了代码,其中在 booksDataStore 上调用了 setValue(item, attribute, value) 方法,'name' 属性的值变更为所选节点。
清单 9. 更新 Dojo 树的节点
function updateNodeToTree()
{
//update data of the bookstore//
booksDataStore.fetchItemByIdentity({
identity: 'AlgorithmsId',
onItem: dojo.hitch(booksDataStore,
function(item) {
booksDataStore.setValue(item, 'name',
'Discrete');
booksDataStore.save({
onComplete: function() {
alert("Update Complete");
}
});
})
|
JsonDataStore 的 setValue(item, attribute, value) 方法更新了当前节点。清单 9 中的代码将 'AlgorithmsId' 节点的名称由 Algorithms 更新到 Discrete。图 6 显示了更新后的树。
图 6. 节点 Algorithms 更新到 Discrete
总而言之,基于 Dojo 和 REST 的 JsonRestStore 为允许高效地获取和显示大量数据集的延迟加载树提供了良好支持。当您使用 JsonRestStore 提供的方法时,就能够轻松地更新、添加和删除 Dojo 树。Dojo 和 JsonRestStore 让您能够利用 Dojo 树上的延迟加载功能。您可以通过将 Dojo 树部件和基于 REST 数据存储完美结合,创建高级的,基于丰富文本的 UIs。
学习
- 阅读更多关于 Dojo dijit.Tree 的资料,了解树如何帮助解决长的、分层的清单。
- 了解更多关于 JsonRestStore 的功能和使用。
- 探索 Dojo API for JsonRestStore。JsonRestStore 使用 Rest 命令(
PUT,POST,或者DELETE)会使所有已保存的修改被发送到服务器。 - “Efficient Lazy Loading of a Tree” 提供了使用 JsonRestStore 构建一个延迟加载的树的例子。
- 了解更多关于 JSON 的信息,它是便于人类读写的轻量级数据交换格式。
-
developerWorks Web development
专区:通过专门关于 Web 技术的文章和教程,扩展您在网站开发方面的技能。
-
developerWorks Ajax 资源中心:这是有关 Ajax 编程模型信息的一站式中心,包括很多文档、教程、论坛、blog、wiki 和新闻。任何 Ajax 的新信息都能在这里找到。
-
developerWorks Web 2.0 资源中心,这是有关 Web 2.0 相关信息的一站式中心,包括大量 Web 2.0 技术文章、教程、下载和相关技术资源。您还可以通过 Web 2.0 新手入门 栏目,迅速了解 Web 2.0 的相关概念。
- 查看 HTML5 专题,了解更多和 HTML5 相关的知识和动向。
获得产品和技术
- 下载 IBM 产品评估试用版软件 或 IBM SOA 人员沙箱,并开始使用来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。
讨论
- 分享您的知识:加入一个关注 web 主题的 developerWorks 群组。
- Roland Barcia 撰写的关于 Web 2.0 和中间件 的博客。
- 关注 developerWorks 成员 关于 web 主题的共享书签。
- 快速找到答案:访问 Web 2.0 Apps 论坛。
- 快速找到答案:访问 Ajax 论坛。
- 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。
