Connecting a Store toa Tree

Connecting a Store toa Tree

Dojo树组件是一个强大的视觉呈现分层数据的工具。本教程中,我们将看到如何去迅速、高效的获取tree组件数据以及向下钻取到嵌套的数据。

原文链接:http://dojotoolkit.org/documentation/tutorials/1.7/store_driven_tree/

困难:中间的   Dojo版本:1.7

1.     简介

DojoTree组件提供了一个复杂的、常见的、直观的分级数据钻取展现。Tree支持分支的懒加载,使得它对于大数据集有很高的可扩展性。当数据有父子关系时,Tree是一个很有用的widget

在这里,我们将会学习如何使用新的Dojo对象树存储接口,去迅速构造数据驱动的树结构。在本指南里,为了易于打开章节和折叠我们不使用的章节,我们将会使用一个提供US政府结构的数据源信息并在一个树里显示信息。我们将会从头开始,创建一个简单的对象存储。用带有懒加载、可拖拽的的数据驱动树来结束,并且实时回应数据的变化。

2.     树与静态存储

一个静态存储很适合于固定大小不能动态改变的树。在这个例子中,单击树节点显示一个相关的图像。

第一步是创建数据。数据源是json编码的数据,可以包含支持信息。在这个例子中,名称用于标签树的每个节点。这棵树有三个节点,每一个都有名称和id

2.1.  数据源

1.  {

2.      "label":"name",

3.      "name": "USGovernment",

4.      "id": "root",

5.      "items": [

6.          {

7.              "name":"Congress",

8.              "id":"congress",

9.          },

10.        {

11.            "name":"Executive",

12.            "id":"exec",

13.        },

14.        {

15.            "name":"Judicial",

16.            "id":"judicial"

17.        }

18.    ]

19.}  

2.2.  Tree

代码读取存储数据,应用数据模型,并将其分配给树小部件。onLoadonClick事件被用于显示相关的图像。

20.dojo.require("dojo.parser");

21.dojo.require("dojo.data.ItemFileReadStore");

22.dojo.require("dijit.Tree");

23.dojo.require("dijit.tree.ForestStoreModel");

24. 

25.// when dojo is loaded and ready

26.dojo.ready(function(){

27. 

28.  // set up the store to get the tree data

29.  var governmentStore = newdojo.data.ItemFileReadStore({

30.    url: "data/static"

31.  });

32. 

33.  // set up the model, assigninggovernmentStore

34.  var governmentModel = newdijit.tree.ForestStoreModel({

35.    store: governmentStore,

36.    query: {"id":"*"},

37.    rootId: "root",

38.    rootLabel: "USGovernment",

39.    childrenAttrs: "items"

40.  });

41. 

42.  // set up the tree, assigning governmentModel

43.  var governmentTree = new dijit.Tree({

44.    model: governmentModel,

45.    onOpenClick: true,

46.    onLoad: function(){

47.      dojo.byId('image').src= '../resources/images/root.jpg';

48.    },

49.    onClick: function(item){

50.      dojo.byId('image').src= '../resources/images/'+item.id+'.jpg';

51.    }

52.  },"divTree");

53.});

注意,我们使用一个ForestStoreModel,它允许多个根节点,所以树或数据可以更方便地从不同的层面获取。

View Demo

3.     树和对象存储

这棵树支持延迟加载分支机构,使其高度可伸缩的大型数据集,这里我们将学习如何使用新的Dojo对象存储接,快速构建树的数据驱动。在这个例子中,我们将使用一个美国政府结构的数据信息。我们要从头开始创建一个简单的对象存储,和一个数据驱动的树懒加载,拖放,实时响应数据的变化。

View Complete Demo

3.1.  Store

我们将首先创建数据源.这将是tree的数据驱动。在这里我们将要使用JsonRest数据源,这有助于延迟加载数据。在这个示例中,我们将展示美国政府的层次结构。这是JsonRest store基本的实例化,用于连接到我们的服务器,以便数据检索:

54.require(["dojo/store/JsonRest"],function(JsonRest) {

55.    usGov = new JsonRest({

56.        target:"data/"

57.    });

58.});

3.2.  添加基本的数据模型

我们将使用我们的数据作为树的存储模型。为了做到这一点,我们还需要定义模型逻辑层次结构,描述在我们的数据。这棵树需要五个模型方法来呈现数据:

getIdentity(object) – 数据源已经提供,通常不需要重新实现。

mayHaveChildren(object) 标记一个对象是否可能有子节点(在加载子节点以前)。在这个例子中我们根据children的属性来标记是否有子节点。

getChildren(parent, onComplete, onError)-检索子节点。在这个例子中,我们将执行一个get()来检索父对象来获取的孩子。一旦父节点完全被加载,我们返回子节点,以数组形式。

getRoot(onItem, onError)-调用来检索的根节点。 onItem回调应该被称为根对象。

getLabel(object)——返回标签对象。在这个例子中,标签只是name属性的对象

现在,让我们看看如何实现定义的数据结构的层次结构, 我们可以非常容易做到这一点,通过JsonRest实例化中定义方法:

59.usGov = JsonRest({

60.    target:"data/",

61.    mayHaveChildren:function(object){

62.        // see ifit has a children property

63.        return"children" in object;

64.    },

65.    getChildren: function(object,onComplete, onError){

66.        //retrieve the full copy of the object

67.        this.get(object.id).then(function(fullObject){

68.            //copy to the original object so it has the children array as well.

69.            object.children= fullObject.children;

70.            //now that we have the full object, we should have an array of children

71.            onComplete(fullObject.children);

72.        },function(error){

73.            //an error occurred, log it, and indicate no children

74.            console.error(error);

75.            onComplete([]);

76.        });

77.    },

78.    getRoot: function(onItem,onError){

79.        // getthe root object, we will do a get() and callback the result

80.        this.get("root").then(onItem,onError);

81.    },

82.    getLabel: function(object){

83.        // justget the name

84.        returnobject.name;

85.    }

86.});

3.3.  创建树与数据源作为数据模型

现在我们可以很容易地填补这个存储进我们的树。

87.require(["dijit/Tree"], function(Tree) {

88.    tree = new Tree({ // create atree

89.            model:usGov // give it the model

90.        },"tree"); // target HTML element's id

91.    tree.startup();

92.});

当这个树 startup时,它将查询我们的模型/存储。它会询问数据源的标签(通过getLabel()),和子节点们(通过调用getChildren())。对于每个孩子,它将呈现标签和添加一个扩展器图标,如果对象可能有孩子(通过mayHaveChildren())。我们的getChildren()getRoot()函数委托给get()调用,这触发请求到服务器(使用数据源的target,连接id传递给get(),get请求的URL)。服务器响应这些请求与JSON来满足模型和树。这是它的样子。

View Demo

4.     延迟加载

利用延迟加载,当装载一个对象以及它的节点时,我们的服务器提供了每个节点的对象。然而,对于每个孩子只有”name”属性(标签),“id“属性(确定对象),和一个boolean属性”children”(如果为true代表有子节点,反之无)。这种方法延迟加载确保只有一个请求是需要每次一个节点被扩展(而不是一个请求的每个子节点扩展节点)。这就是我们的服务器返回的对象(得到数据/ root):

93. {

94.    "name": "USGovernment",

95.    "id": "root",

96.    "children": [

97.        {

98.            "name":"Congress",

99.            "id":"congress",

100.             "children":true

101.         },

102.         {

103.             "name":"Executive",

104.             "id":"exec",

105.             "children":true

106.         },

107.         {

108.             "name":"Judicial",

109.             "id":"judicial"

110.         }

111.    ]

112. }

然后,当我们点击扩展一个节点时,树将请求目标对象的子节点。如果我们点击Executive 节点,store将使用目标对象的id(“exec)请求完整的对象,触发请求Get data/ exec。然后服务器响应:

113. {

114.     "name":"Executive",

115.     "id": "exec",

116.     "children": [

117.         {

118.             "name":"President",

119.             "id":"pres"

120.         },

121.         {

122.             "name":"Vice President",

123.             "id":"vice-pres"

124.         },

125.         {

126.             "name":"Secretary of State",

127.             "id":"state"

128.         },

129.         {

130.             "name":"Cabinet",

131.             "id":"cabinet",

132.             "children":true

133.         }

134.     ]

135. }

在这个相应里面,你可以看到只有Cabinet有子节点。

4.1.  用户修改树

树小部件对拖放修改具有良好的支持。如果我们想通过拖放修改我们的数据,我们可以实现pasteItem()方法并设置树拖放控制器即可。首先,让我们实现pasteItem()。当拖放操作发生时,这个方法被调用。这个pasteItem()方法被调用时包含以下几个参数:

child 这个子节点是被粘贴,拖放的。

oldParent  子节点被脱离的父节点。

newParent 子节点被拖放到的位置,该位置就是该子节点的新父节点。

bCopy - 表示如果孩子应该被复制而不是移动。

insertIndex 被拖放的节点(1)拖放到别的节点(2)中,则2节点变成了新的父节点(如果存储支持排序)

实现 pasteItem()的基本方法是非常简单的。在我们的示例中,我们只是想删除子对象从oldParent数组并添加子对象数组作为newParent的子对象。我们可以通过寻找孩子的索引在oldParentChildren数组,使用splice()来删除它,然后使用splice()将其放置在newParent中。然后我们调用put()为每个父对象来保存修改。

然而, ,我们也需要考虑一些并发症。首先,父对象可能会或可能不会完全下载对象。在我们的延迟加载方案中,只有完整对象有孩子数组。因此,我们将对每个父节点执行一个get(),以确保我们有完整的对象。其次,因为有可能是替代对象的副本,我们不能做直接的indexOf()调用孩子数组,找到子对象,所以我们需要扫描阵列相匹配的ID找到一个对象.

考虑到这些因素,我们可以创建我们的pasteItem()实现;

136. usGov = new JsonRest({

137.     pasteItem: function(child,oldParent, newParent, bCopy, insertIndex){

138.         // makethis store available in all the inner functions

139.         var store= this;

140.         // getthe full oldParent object

141.         store.get(oldParent.id).then(function(oldParent){

142.                 //get the full newParent object

143.                 returnstore.get(newParent.id)

144.             }).then(function(newParent){

145.                 //get the oldParent's children and scan through it find the child object

146.                 varoldChildren = oldParent.children;

147.                 dojo.some(oldChildren,function(oldChild, i){

148.                     //it matches if the ids match

149.                     if(oldChild.id== child.id){

150.                         //found the child, now remove it from the children array

151.                         oldChildren.splice(i,1);

152.                         returntrue; // done, break out of the some() loop

153.                     }

154.                 });

155.                 //do a put to save the oldParent with the modified childrens array

156.                 store.put(oldParent);

157.                 //now insert the child object into the new parent,

158.                 //using the insertIndex if available

159.                 newParent.children.splice(insertIndex|| 0, 0, child);

160.                 //save changes to the newParent

161.                 store.put(newParent);

162.             },function(error){

163.                 //catch and report any errors

164.                 alert("Erroroccurred (this demo is not hooked up to a real database, so this is expected):" + error);

165.             });

166.         });

167.     },

168.     ...

4.2.  为树配置拖放

当我们定义拖放树的时候,需要时使用标准的dijit/tree/dndSource作为控制器:

169. require(["dijit/Tree","dijit/tree/dndSource", "dojo/domReady!"], function(Tree,dndSource) {

170.     tree = new Tree({

171.         model:usGov,

172.         // definethe drag-n-drop controller

173.         dndController:dndSource

174.     }, "tree");

175.     tree.startup();

176. });

现在当我们进行拖放操作的时候会触发并且实现pasteItem()方法,然后引起子节点数组被修改和保存。在JsonRest store中,这些修改保存调用put()将会触发Http Put请求去保存数据到服务。

4.3.  Notifications

我们需要通知树子节点的变化。树遵循标准的MVC数据模型变化,而不是控制器的操作原则。这是非常强大的,因为无论是什么引起的变化,视图中的数据都会做出反应(直接编程,拖放等)。这棵树侦听“onChildrenChange”“onChange”,“onDelete”事件。Store API规定数据更新发生通过它的put()方法。我们可以扩展put()调用这些模型改变方法(触发树事件),然后调用原始的put()方法来完成行为上的存储。同样我们可以调用onDelete事件的remove()方法:

177. usGov = new JsonRest({

178.     put: function(object, options){

179.         // firethe onChildrenChange event

180.         this.onChildrenChange(object,object.children);

181.         // firethe onChange event

182.         this.onChange(object);

183.         //execute the default action

184.         return JsonRest.prototype.put.apply(this,arguments);

185.     },

186.     remove: function(id){

187.         // Wecall onDelete to signal to the tree to remove the child. The

188.         //remove(id) gets and id, but onDelete expects an object, so we create

189.         // a fakeobject that has an identity matching the id of the object we

190.         // areremoving.

191.         this.onDelete({id:id});

192.         // notethat you could alternately wait for this inherited add function to

193.         // finish(using .then()) if you don't want the event to fire until it is

194.         // confirmedby the server

195.     },

196.     // we also add event stubs sothese methods can be

197.     // called before the listenersare applied

198.     onChildrenChange:function(parent, children){

199.         // firedwhen the set of children for an object changes

200.     },

201.     onChange: function(object){

202.         // firedwhen the properties of an object change

203.     },

204.     onDelete: function(object){

205.         // firedwhen an object is deleted

206.     },

207.     ...

我们现在已经定义了我们的数据模型的方法,我们可以使用存储与树拖放。我们可以查看教程演示,但是请注意,这个演示没有实现任何响应HTTP PUT请求。演示是静态文件,所以没有什么是真的改变了。如果你做多个拖放操作你会看到对象出现在老地方,因为服务器是不断地回应相同的静态数据。

View Demo

4.4.  编程数据的变化

前面讲过the Tree/model interface is designed so thatthe Tree responds to changes regardless of thetrigger. 因此,添加一个新的子节点,我们可以简单地插入一个子对象到父节点的孩子数组,保存它调用put(),这棵树会自动响应。在这个演示中,一个按钮触发器添加一个子对象使用以下代码

208. // get the selected object from the tree

209. var selectedObject =tree.get("selectedItems")[0];

210. // check ensure an object is selected

211. if(!selectedObject){

212.     // alert and return if noselected object

213.     return alert("No objectselected");

214. }

215. // get the full copy of the object

216. usGov.get(selectedObject.id).then(function(selectedObject){

217.     // add a new child

218.     selectedObject.children.push({

219.         name:"New child",

220.         id:Math.random()

221.     });

222.     // save it with a put(). The tree will automatically update the UI

223.     usGov.put(selectedObject);

224. });

而且,我们可以用同样的方法把节点删除。我们也可以改变对象的属性,比如名称(标签的节点)。在这个演示中,我们倾听双击提示输入一个新名字,对象:

225. tree.on("dblclick", function(object){

226.     // node was double clicked,prompt for a new name

227.     object.name = prompt("Entera new name for the object");

228.     // save the change, again thetree auto-updates

229.     usGov.put(object);

230. }, true);

最后,我们最终得到以下:

231. require(["dojo/store/JsonRest","dojo/store/Observable", "dijit/Tree","dijit/tree/dndSource", "dojo/query","dojo/domReady!"],

232.     function(JsonRest, Observable,Tree, dndSource, query) {

233.  

234.         usGov =JsonRest({

235.             target:"data/",

236.             mayHaveChildren:function(object){

237.                 //see if it has a children property

238.                 return"children" in object;

239.             },

240.             getChildren:function(object, onComplete, onError){

241.                 //retrieve the full copy of the object

242.                 this.get(object.id).then(function(fullObject){

243.                     //copy to the original object so it has the children array as well.

244.                     object.children= fullObject.children;

245.                     //now that we have the full object, we should have an array of children

246.                     onComplete(fullObject.children);

247.                 },function(error){

248.                     //an error occurred, log it, and indicate no children

249.                     console.error(error);

250.                     onComplete([]);

251.                 });

252.             },

253.             getRoot:function(onItem, onError){

254.                 //get the root object, we will do a get() and callback the result

255.                 this.get("root").then(onItem,onError);

256.             },

257.             getLabel:function(object){

258.                 //just get the name

259.                 returnobject.name;

260.             },

261.  

262.             pasteItem:function(child, oldParent, newParent, bCopy, insertIndex){

263.                 varstore = this;

264.                 store.get(oldParent.id).then(function(oldParent){

265.                     store.get(newParent.id).then(function(newParent){

266.                         varoldChildren = oldParent.children;

267.                         dojo.some(oldChildren,function(oldChild, i){

268.                             if(oldChild.id== child.id){

269.                                 oldChildren.splice(i,1);

270.                                 returntrue; // done

271.                             }

272.                         });

273.                         store.put(oldParent);

274.                         newParent.children.splice(insertIndex|| 0, 0, child);

275.                         store.put(newParent);

276.                     },function(error){

277.                         alert("Erroroccurred (this demo is not hooked up to a real database, so this is expected):" + error);

278.                     });

279.                 });

280.             },

281.             put:function(object, options){

282.                 this.onChildrenChange(object,object.children);

283.                 this.onChange(object);

284.                 returnJsonRest.prototype.put.apply(this, arguments);

285.             },

286.             remove:function(id){

287.                 this.onDelete({id:id});

288.                 returnJsonRest.prototype.remove.apply(this, arguments);

289.             }

290.         });

291.         tree =new Tree({

292.             model:usGov,

293.             dndController:dndSource

294.         },"tree"); // make sure you have a target HTML element with this id

295.         tree.startup();

296.         query("#add-new-child").on("click",function(){

297.             varselectedObject = tree.get("selectedItems")[0];

298.             if(!selectedObject){

299.                 returnalert("No object selected");

300.             }

301.             usGov.get(selectedObject.id).then(function(selectedObject){

302.                 selectedObject.children.push({

303.                     name:"New child",

304.                     id:Math.random()

305.                 });

306.                 usGov.put(selectedObject);

307.             });

308.  

309.         });

310.         query("#remove").on("click",function(){

311.             varselectedObject = tree.get("selectedItems")[0];

312.             if(!selectedObject){

313.                 returnalert("No object selected");

314.             }

315.             usGov.remove(selectedObject.id);

316.         });

317.         tree.on("dblclick",function(object){

318.             object.name= prompt("Enter a new name for the object");

319.             usGov.put(object);

320.         }, true);

321. });

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值