4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
17 <body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-data-TreeStore'>/**
19 </span> * The TreeStore is a store implementation that is backed by by an {@link Ext.data.Tree}.
20 * It provides convenience methods for loading nodes, as well as the ability to use
21 * the hierarchical tree structure combined with a store. This class is generally used
22 * in conjunction with {@link Ext.tree.Panel}. This class also relays many events from
23 * the Tree for convenience.
27 * If no Model is specified, an implicit model will be created that implements {@link Ext.data.NodeInterface}.
28 * The standard Tree fields will also be copied onto the Model for maintaining their state.
30 * # Reading Nested Data
32 * For the tree to read nested data, the {@link Ext.data.reader.Reader} must be configured with a root property,
33 * so the reader can find nested data for each node. If a root is not specified, it will default to
36 Ext.define('Ext.data.TreeStore', {
37 extend: 'Ext.data.AbstractStore',
39 requires: ['Ext.data.Tree', 'Ext.data.NodeInterface', 'Ext.data.NodeStore'],
41 <span id='Ext-data-TreeStore-cfg-root'> /**
42 </span> * @cfg {Ext.data.Model/Ext.data.NodeInterface/Object} root
43 * The root node for this store. For example:
47 * text: "My Root",
49 * { text: "Child 1", leaf: true },
50 * { text: "Child 2", expanded: true, children: [
51 * { text: "GrandChild", leaf: true }
56 * Setting the `root` config option is the same as calling {@link #setRootNode}.
59 <span id='Ext-data-TreeStore-cfg-clearOnLoad'> /**
60 </span> * @cfg {Boolean} clearOnLoad
61 * Remove previously existing child nodes before loading. Default to true.
65 <span id='Ext-data-TreeStore-cfg-nodeParam'> /**
66 </span> * @cfg {String} nodeParam
67 * The name of the parameter sent to the server which contains the identifier of the node.
72 <span id='Ext-data-TreeStore-cfg-defaultRootId'> /**
73 </span> * @cfg {String} defaultRootId
74 * The default root id. Defaults to 'root'
76 defaultRootId: 'root',
78 <span id='Ext-data-TreeStore-cfg-defaultRootProperty'> /**
79 </span> * @cfg {String} defaultRootProperty
80 * The root property to specify on the reader if one is not explicitly defined.
82 defaultRootProperty: 'children',
84 <span id='Ext-data-TreeStore-cfg-folderSort'> /**
85 </span> * @cfg {Boolean} folderSort
86 * Set to true to automatically prepend a leaf sorter. Defaults to `undefined`.
90 constructor: function(config) {
95 config = Ext.apply({}, config);
97 <span id='Ext-data-TreeStore-property-fields'> /**
98 </span> * If we have no fields declare for the store, add some defaults.
99 * These will be ignored if a model is explicitly specified.
101 fields = config.fields || me.fields;
103 config.fields = [{name: 'text', type: 'string'}];
106 me.callParent([config]);
108 // We create our data tree.
109 me.tree = Ext.create('Ext.data.Tree');
111 me.relayEvents(me.tree, [
112 <span id='Ext-data-TreeStore-event-append'> /**
113 </span> * @event append
114 * Fires when a new child node is appended to a node in this store's tree.
115 * @param {Tree} tree The owner tree
116 * @param {Node} parent The parent node
117 * @param {Node} node The newly appended node
118 * @param {Number} index The index of the newly appended node
122 <span id='Ext-data-TreeStore-event-remove'> /**
123 </span> * @event remove
124 * Fires when a child node is removed from a node in this store's tree.
125 * @param {Tree} tree The owner tree
126 * @param {Node} parent The parent node
127 * @param {Node} node The child node removed
131 <span id='Ext-data-TreeStore-event-move'> /**
132 </span> * @event move
133 * Fires when a node is moved to a new location in the store's tree
134 * @param {Tree} tree The owner tree
135 * @param {Node} node The node moved
136 * @param {Node} oldParent The old parent of this node
137 * @param {Node} newParent The new parent of this node
138 * @param {Number} index The index it was moved to
142 <span id='Ext-data-TreeStore-event-insert'> /**
143 </span> * @event insert
144 * Fires when a new child node is inserted in a node in this store's tree.
145 * @param {Tree} tree The owner tree
146 * @param {Node} parent The parent node
147 * @param {Node} node The child node inserted
148 * @param {Node} refNode The child node the node was inserted before
152 <span id='Ext-data-TreeStore-event-beforeappend'> /**
153 </span> * @event beforeappend
154 * Fires before a new child is appended to a node in this store's tree, return false to cancel the append.
155 * @param {Tree} tree The owner tree
156 * @param {Node} parent The parent node
157 * @param {Node} node The child node to be appended
159 "beforeappend",
161 <span id='Ext-data-TreeStore-event-beforeremove'> /**
162 </span> * @event beforeremove
163 * Fires before a child is removed from a node in this store's tree, return false to cancel the remove.
164 * @param {Tree} tree The owner tree
165 * @param {Node} parent The parent node
166 * @param {Node} node The child node to be removed
168 "beforeremove",
170 <span id='Ext-data-TreeStore-event-beforemove'> /**
171 </span> * @event beforemove
172 * Fires before a node is moved to a new location in the store's tree. Return false to cancel the move.
173 * @param {Tree} tree The owner tree
174 * @param {Node} node The node being moved
175 * @param {Node} oldParent The parent of the node
176 * @param {Node} newParent The new parent the node is moving to
177 * @param {Number} index The index it is being moved to
179 "beforemove",
181 <span id='Ext-data-TreeStore-event-beforeinsert'> /**
182 </span> * @event beforeinsert
183 * Fires before a new child is inserted in a node in this store's tree, return false to cancel the insert.
184 * @param {Tree} tree The owner tree
185 * @param {Node} parent The parent node
186 * @param {Node} node The child node to be inserted
187 * @param {Node} refNode The child node the node is being inserted before
189 "beforeinsert",
191 <span id='Ext-data-TreeStore-event-expand'> /**
192 </span> * @event expand
193 * Fires when this node is expanded.
194 * @param {Node} this The expanding node
198 <span id='Ext-data-TreeStore-event-collapse'> /**
199 </span> * @event collapse
200 * Fires when this node is collapsed.
201 * @param {Node} this The collapsing node
203 "collapse",
205 <span id='Ext-data-TreeStore-event-beforeexpand'> /**
206 </span> * @event beforeexpand
207 * Fires before this node is expanded.
208 * @param {Node} this The expanding node
210 "beforeexpand",
212 <span id='Ext-data-TreeStore-event-beforecollapse'> /**
213 </span> * @event beforecollapse
214 * Fires before this node is collapsed.
215 * @param {Node} this The collapsing node
217 "beforecollapse",
219 <span id='Ext-data-TreeStore-event-sort'> /**
220 </span> * @event sort
221 * Fires when this TreeStore is sorted.
222 * @param {Node} node The node that is sorted.
226 <span id='Ext-data-TreeStore-event-rootchange'> /**
227 </span> * @event rootchange
228 * Fires whenever the root node is changed in the tree.
229 * @param {Ext.data.Model} root The new root
231 "rootchange"
236 remove: me.onNodeRemove,
237 // this event must follow the relay to beforeitemexpand to allow users to
238 // cancel the expand:
239 beforeexpand: me.onBeforeNodeExpand,
240 beforecollapse: me.onBeforeNodeCollapse,
241 append: me.onNodeAdded,
242 insert: me.onNodeAdded
250 me.setRootNode(root);
254 <span id='Ext-data-TreeStore-event-rootchange'> /**
255 </span> * @event rootchange
256 * Fires when the root node on this TreeStore is changed.
257 * @param {Ext.data.TreeStore} store This TreeStore
258 * @param {Node} The new root node.
263 //<deprecated since=0.99>
264 if (Ext.isDefined(me.nodeParameter)) {
265 if (Ext.isDefined(Ext.global.console)) {
266 Ext.global.console.warn('Ext.data.TreeStore: nodeParameter has been deprecated. Please use nodeParam instead.');
268 me.nodeParam = me.nodeParameter;
269 delete me.nodeParameter;
271 //</deprecated>
275 setProxy: function(proxy) {
279 if (proxy instanceof Ext.data.proxy.Proxy) {
280 // proxy instance, check if a root was set
281 needsRoot = Ext.isEmpty(proxy.getReader().root);
282 } else if (Ext.isString(proxy)) {
283 // string type, means a reader can't be set
286 // object, check if a reader and a root were specified.
287 reader = proxy.reader;
288 needsRoot = !(reader && !Ext.isEmpty(reader.root));
290 proxy = this.callParent(arguments);
292 reader = proxy.getReader();
293 reader.root = this.defaultRootProperty;
295 reader.buildExtractors(true);
300 onBeforeSort: function() {
301 if (this.folderSort) {
305 }, 'prepend', false);
309 <span id='Ext-data-TreeStore-method-onBeforeNodeExpand'> /**
310 </span> * Called before a node is expanded.
312 * @param {Ext.data.NodeInterface} node The node being expanded.
313 * @param {Function} callback The function to run after the expand finishes
314 * @param {Object} scope The scope in which to run the callback function
316 onBeforeNodeExpand: function(node, callback, scope) {
317 if (node.isLoaded()) {
318 Ext.callback(callback, scope || node, [node.childNodes]);
320 else if (node.isLoading()) {
321 this.on('load', function() {
322 Ext.callback(callback, scope || node, [node.childNodes]);
323 }, this, {single: true});
328 callback: function() {
329 Ext.callback(callback, scope || node, [node.childNodes]);
336 getNewRecords: function() {
337 return Ext.Array.filter(this.tree.flatten(), this.filterNew);
341 getUpdatedRecords: function() {
342 return Ext.Array.filter(this.tree.flatten(), this.filterUpdated);
345 <span id='Ext-data-TreeStore-method-onBeforeNodeCollapse'> /**
346 </span> * Called before a node is collapsed.
348 * @param {Ext.data.NodeInterface} node The node being collapsed.
349 * @param {Function} callback The function to run after the collapse finishes
350 * @param {Object} scope The scope in which to run the callback function
352 onBeforeNodeCollapse: function(node, callback, scope) {
353 callback.call(scope || node, node.childNodes);
356 onNodeRemove: function(parent, node) {
357 var removed = this.removed;
359 if (!node.isReplace && Ext.Array.indexOf(removed, node) == -1) {
364 onNodeAdded: function(parent, node) {
365 var proxy = this.getProxy(),
366 reader = proxy.getReader(),
367 data = node.raw || node.data,
370 Ext.Array.remove(this.removed, node);
372 if (!node.isLeaf() && !node.isLoaded()) {
373 dataRoot = reader.getRoot(data);
375 this.fillNode(node, reader.extractData(dataRoot));
376 delete data[reader.root];
381 <span id='Ext-data-TreeStore-method-setRootNode'> /**
382 </span> * Sets the root node for this store. See also the {@link #root} config option.
383 * @param {Ext.data.Model/Ext.data.NodeInterface/Object} root
384 * @return {Ext.data.NodeInterface} The new root
386 setRootNode: function(root) {
391 // create a default rootNode and create internal data struct.
393 id: me.defaultRootId,
397 root = Ext.ModelManager.create(root, me.model);
399 Ext.data.NodeInterface.decorate(root);
401 // Because we have decorated the model with new fields,
402 // we need to build new extactor functions on the reader.
403 me.getProxy().getReader().buildExtractors(true);
405 // When we add the root to the tree, it will automaticaly get the NodeInterface
406 me.tree.setRootNode(root);
408 // If the user has set expanded: true on the root, we want to call the expand function
409 if (!root.isLoaded() && root.isExpanded()) {
418 <span id='Ext-data-TreeStore-method-getRootNode'> /**
419 </span> * Returns the root node for this tree.
420 * @return {Ext.data.NodeInterface}
422 getRootNode: function() {
423 return this.tree.getRootNode();
426 <span id='Ext-data-TreeStore-method-getNodeById'> /**
427 </span> * Returns the record node by id
428 * @return {Ext.data.NodeInterface}
430 getNodeById: function(id) {
431 return this.tree.getNodeById(id);
434 <span id='Ext-data-TreeStore-method-load'> /**
435 </span> * Loads the Store using its configured {@link #proxy}.
436 * @param {Object} options Optional config object. This is passed into the {@link Ext.data.Operation Operation}
437 * object that is created and then sent to the proxy's {@link Ext.data.proxy.Proxy#read} function.
438 * The options can also contain a node, which indicates which node is to be loaded. If not specified, it will
439 * default to the root node.
441 load: function(options) {
442 options = options || {};
443 options.params = options.params || {};
446 node = options.node || me.tree.getRootNode(),
449 // If there is not a node it means the user hasnt defined a rootnode yet. In this case lets just
450 // create one for them.
452 node = me.setRootNode({
457 if (me.clearOnLoad) {
461 Ext.applyIf(options, {
464 options.params[me.nodeParam] = node ? node.getId() : 'root';
467 node.set('loading', true);
470 return me.callParent([options]);
474 <span id='Ext-data-TreeStore-method-fillNode'> /**
475 </span> * Fills a node with a series of child records.
477 * @param {Ext.data.NodeInterface} node The node to fill
478 * @param {Array} records The records to add
480 fillNode: function(node, records) {
482 ln = records ? records.length : 0,
483 i = 0, sortCollection;
485 if (ln && me.sortOnLoad && !me.remoteSort && me.sorters && me.sorters.items) {
486 sortCollection = Ext.create('Ext.util.MixedCollection');
487 sortCollection.addAll(records);
488 sortCollection.sort(me.sorters.items);
489 records = sortCollection.items;
492 node.set('loaded', true);
493 for (; i < ln; i++) {
494 node.appendChild(records[i], undefined, true);
501 onProxyLoad: function(operation) {
503 successful = operation.wasSuccessful(),
504 records = operation.getRecords(),
505 node = operation.node;
507 node.set('loading', false);
509 records = me.fillNode(node, records);
512 me.fireEvent('read', me, operation.node, records, successful);
513 me.fireEvent('load', me, operation.node, records, successful);
514 //this is a callback that would have been passed to the 'read' function and is optional
515 Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
518 <span id='Ext-data-TreeStore-method-onCreateRecords'> /**
519 </span> * Creates any new records when a write is returned from the server.
521 * @param {Array} records The array of new records
522 * @param {Ext.data.Operation} operation The operation that just completed
523 * @param {Boolean} success True if the operation was successful
525 onCreateRecords: function(records, operation, success) {
528 length = records.length,
529 originalRecords = operation.records,
536 * Loop over each record returned from the server. Assume they are
537 * returned in order of how they were sent. If we find a matching
538 * record, replace it with the newly created one.
540 for (; i < length; ++i) {
542 original = originalRecords[i];
544 parentNode = original.parentNode;
546 // prevent being added to the removed cache
547 original.isReplace = true;
548 parentNode.replaceChild(record, original);
549 delete original.isReplace;
551 record.phantom = false;
557 <span id='Ext-data-TreeStore-method-onUpdateRecords'> /**
558 </span> * Updates any records when a write is returned from the server.
560 * @param {Array} records The array of updated records
561 * @param {Ext.data.Operation} operation The operation that just completed
562 * @param {Boolean} success True if the operation was successful
564 onUpdateRecords: function(records, operation, success){
568 length = records.length,
574 for (; i < length; ++i) {
576 original = me.tree.getNodeById(record.getId());
577 parentNode = original.parentNode;
579 // prevent being added to the removed cache
580 original.isReplace = true;
581 parentNode.replaceChild(record, original);
582 original.isReplace = false;
588 <span id='Ext-data-TreeStore-method-onDestroyRecords'> /**
589 </span> * Removes any records when a write is returned from the server.
591 * @param {Array} records The array of removed records
592 * @param {Ext.data.Operation} operation The operation that just completed
593 * @param {Boolean} success True if the operation was successful
595 onDestroyRecords: function(records, operation, success){
602 removeAll: function() {
603 this.getRootNode().destroy(true);
604 this.fireEvent('clear', this);
608 doSort: function(sorterFn) {
611 //the load function will pick up the new sorters and request the sorted data from the proxy
614 me.tree.sort(sorterFn, true);
615 me.fireEvent('datachanged', me);
617 me.fireEvent('sort', me);