source: Dev/trunk/src/client/dijit/tree/ForestStoreModel.js

Last change on this file was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

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