ForestStoreModel.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. define("dijit/tree/ForestStoreModel", [
  2. "dojo/_base/array", // array.indexOf array.some
  3. "dojo/_base/declare", // declare
  4. "dojo/_base/lang", // lang.hitch
  5. "dojo/_base/window", // win.global
  6. "./TreeStoreModel"
  7. ], function(array, declare, lang, win, TreeStoreModel){
  8. /*=====
  9. var TreeStoreModel = dijit.tree.TreeStoreModel;
  10. =====*/
  11. // module:
  12. // dijit/tree/ForestStoreModel
  13. // summary:
  14. // Interface between a dijit.Tree and a dojo.data store that doesn't have a root item,
  15. // a.k.a. a store that has multiple "top level" items.
  16. return declare("dijit.tree.ForestStoreModel", TreeStoreModel, {
  17. // summary:
  18. // Interface between a dijit.Tree and a dojo.data store that doesn't have a root item,
  19. // a.k.a. a store that has multiple "top level" items.
  20. //
  21. // description
  22. // Use this class to wrap a dojo.data store, making all the items matching the specified query
  23. // appear as children of a fabricated "root item". If no query is specified then all the
  24. // items returned by fetch() on the underlying store become children of the root item.
  25. // This class allows dijit.Tree to assume a single root item, even if the store doesn't have one.
  26. //
  27. // When using this class the developer must override a number of methods according to their app and
  28. // data, including:
  29. // - onNewRootItem
  30. // - onAddToRoot
  31. // - onLeaveRoot
  32. // - onNewItem
  33. // - onSetItem
  34. // Parameters to constructor
  35. // rootId: String
  36. // ID of fabricated root item
  37. rootId: "$root$",
  38. // rootLabel: String
  39. // Label of fabricated root item
  40. rootLabel: "ROOT",
  41. // query: String
  42. // Specifies the set of children of the root item.
  43. // example:
  44. // | {type:'continent'}
  45. query: null,
  46. // End of parameters to constructor
  47. constructor: function(params){
  48. // summary:
  49. // Sets up variables, etc.
  50. // tags:
  51. // private
  52. // Make dummy root item
  53. this.root = {
  54. store: this,
  55. root: true,
  56. id: params.rootId,
  57. label: params.rootLabel,
  58. children: params.rootChildren // optional param
  59. };
  60. },
  61. // =======================================================================
  62. // Methods for traversing hierarchy
  63. mayHaveChildren: function(/*dojo.data.Item*/ item){
  64. // summary:
  65. // Tells if an item has or may have children. Implementing logic here
  66. // avoids showing +/- expando icon for nodes that we know don't have children.
  67. // (For efficiency reasons we may not want to check if an element actually
  68. // has children until user clicks the expando node)
  69. // tags:
  70. // extension
  71. return item === this.root || this.inherited(arguments);
  72. },
  73. getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ callback, /*function*/ onError){
  74. // summary:
  75. // Calls onComplete() with array of child items of given parent item, all loaded.
  76. if(parentItem === this.root){
  77. if(this.root.children){
  78. // already loaded, just return
  79. callback(this.root.children);
  80. }else{
  81. this.store.fetch({
  82. query: this.query,
  83. onComplete: lang.hitch(this, function(items){
  84. this.root.children = items;
  85. callback(items);
  86. }),
  87. onError: onError
  88. });
  89. }
  90. }else{
  91. this.inherited(arguments);
  92. }
  93. },
  94. // =======================================================================
  95. // Inspecting items
  96. isItem: function(/* anything */ something){
  97. return (something === this.root) ? true : this.inherited(arguments);
  98. },
  99. fetchItemByIdentity: function(/* object */ keywordArgs){
  100. if(keywordArgs.identity == this.root.id){
  101. var scope = keywordArgs.scope?keywordArgs.scope:win.global;
  102. if(keywordArgs.onItem){
  103. keywordArgs.onItem.call(scope, this.root);
  104. }
  105. }else{
  106. this.inherited(arguments);
  107. }
  108. },
  109. getIdentity: function(/* item */ item){
  110. return (item === this.root) ? this.root.id : this.inherited(arguments);
  111. },
  112. getLabel: function(/* item */ item){
  113. return (item === this.root) ? this.root.label : this.inherited(arguments);
  114. },
  115. // =======================================================================
  116. // Write interface
  117. newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
  118. // summary:
  119. // Creates a new item. See dojo.data.api.Write for details on args.
  120. // Used in drag & drop when item from external source dropped onto tree.
  121. if(parent === this.root){
  122. this.onNewRootItem(args);
  123. return this.store.newItem(args);
  124. }else{
  125. return this.inherited(arguments);
  126. }
  127. },
  128. onNewRootItem: function(/* dojo.dnd.Item */ /*===== args =====*/){
  129. // summary:
  130. // User can override this method to modify a new element that's being
  131. // added to the root of the tree, for example to add a flag like root=true
  132. },
  133. pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
  134. // summary:
  135. // Move or copy an item from one parent item to another.
  136. // Used in drag & drop
  137. if(oldParentItem === this.root){
  138. if(!bCopy){
  139. // It's onLeaveRoot()'s responsibility to modify the item so it no longer matches
  140. // this.query... thus triggering an onChildrenChange() event to notify the Tree
  141. // that this element is no longer a child of the root node
  142. this.onLeaveRoot(childItem);
  143. }
  144. }
  145. this.inherited(arguments, [childItem,
  146. oldParentItem === this.root ? null : oldParentItem,
  147. newParentItem === this.root ? null : newParentItem,
  148. bCopy,
  149. insertIndex
  150. ]);
  151. if(newParentItem === this.root){
  152. // It's onAddToRoot()'s responsibility to modify the item so it matches
  153. // this.query... thus triggering an onChildrenChange() event to notify the Tree
  154. // that this element is now a child of the root node
  155. this.onAddToRoot(childItem);
  156. }
  157. },
  158. // =======================================================================
  159. // Handling for top level children
  160. onAddToRoot: function(/* item */ item){
  161. // summary:
  162. // Called when item added to root of tree; user must override this method
  163. // to modify the item so that it matches the query for top level items
  164. // example:
  165. // | store.setValue(item, "root", true);
  166. // tags:
  167. // extension
  168. console.log(this, ": item ", item, " added to root");
  169. },
  170. onLeaveRoot: function(/* item */ item){
  171. // summary:
  172. // Called when item removed from root of tree; user must override this method
  173. // to modify the item so it doesn't match the query for top level items
  174. // example:
  175. // | store.unsetAttribute(item, "root");
  176. // tags:
  177. // extension
  178. console.log(this, ": item ", item, " removed from root");
  179. },
  180. // =======================================================================
  181. // Events from data store
  182. _requeryTop: function(){
  183. // reruns the query for the children of the root node,
  184. // sending out an onSet notification if those children have changed
  185. var oldChildren = this.root.children || [];
  186. this.store.fetch({
  187. query: this.query,
  188. onComplete: lang.hitch(this, function(newChildren){
  189. this.root.children = newChildren;
  190. // If the list of children or the order of children has changed...
  191. if(oldChildren.length != newChildren.length ||
  192. array.some(oldChildren, function(item, idx){ return newChildren[idx] != item;})){
  193. this.onChildrenChange(this.root, newChildren);
  194. }
  195. })
  196. });
  197. },
  198. onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
  199. // summary:
  200. // Handler for when new items appear in the store. Developers should override this
  201. // method to be more efficient based on their app/data.
  202. // description:
  203. // Note that the default implementation requeries the top level items every time
  204. // a new item is created, since any new item could be a top level item (even in
  205. // addition to being a child of another item, since items can have multiple parents).
  206. //
  207. // If developers can detect which items are possible top level items (based on the item and the
  208. // parentInfo parameters), they should override this method to only call _requeryTop() for top
  209. // level items. Often all top level items have parentInfo==null, but
  210. // that will depend on which store you use and what your data is like.
  211. // tags:
  212. // extension
  213. this._requeryTop();
  214. this.inherited(arguments);
  215. },
  216. onDeleteItem: function(/*Object*/ item){
  217. // summary:
  218. // Handler for delete notifications from underlying store
  219. // check if this was a child of root, and if so send notification that root's children
  220. // have changed
  221. if(array.indexOf(this.root.children, item) != -1){
  222. this._requeryTop();
  223. }
  224. this.inherited(arguments);
  225. },
  226. onSetItem: function(/* item */ item,
  227. /* attribute-name-string */ attribute,
  228. /* object | array */ oldValue,
  229. /* object | array */ newValue){
  230. // summary:
  231. // Updates the tree view according to changes to an item in the data store.
  232. // Developers should override this method to be more efficient based on their app/data.
  233. // description:
  234. // Handles updates to an item's children by calling onChildrenChange(), and
  235. // other updates to an item by calling onChange().
  236. //
  237. // Also, any change to any item re-executes the query for the tree's top-level items,
  238. // since this modified item may have started/stopped matching the query for top level items.
  239. //
  240. // If possible, developers should override this function to only call _requeryTop() when
  241. // the change to the item has caused it to stop/start being a top level item in the tree.
  242. // tags:
  243. // extension
  244. this._requeryTop();
  245. this.inherited(arguments);
  246. }
  247. });
  248. });