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> * @class Ext.data.TreeStore
20 * @extends Ext.data.AbstractStore
22 * The TreeStore is a store implementation that is backed by by an {@link Ext.data.Tree}.
23 * It provides convenience methods for loading nodes, as well as the ability to use
24 * the hierarchical tree structure combined with a store. This class is generally used
25 * in conjunction with {@link Ext.tree.Panel}. This class also relays many events from
26 * the Tree for convenience.
29 * If no Model is specified, an implicit model will be created that implements {@link Ext.data.NodeInterface}.
30 * The standard Tree fields will also be copied onto the Model for maintaining their state.
32 * ## Reading Nested Data
33 * For the tree to read nested data, the {@link Ext.data.Reader} must be configured with a root property,
34 * so the reader can find nested data for each node. If a root is not specified, it will default to
37 Ext.define('Ext.data.TreeStore', {
38 extend: 'Ext.data.AbstractStore',
40 requires: ['Ext.data.Tree', 'Ext.data.NodeInterface', 'Ext.data.NodeStore'],
42 <span id='Ext-data-TreeStore-cfg-clearOnLoad'> /**
43 </span> * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
44 * child nodes before loading.
48 <span id='Ext-data-TreeStore-cfg-nodeParam'> /**
49 </span> * @cfg {String} nodeParam The name of the parameter sent to the server which contains
50 * the identifier of the node. Defaults to <tt>'node'</tt>.
54 <span id='Ext-data-TreeStore-cfg-defaultRootId'> /**
55 </span> * @cfg {String} defaultRootId
56 * The default root id. Defaults to 'root'
58 defaultRootId: 'root',
60 <span id='Ext-data-TreeStore-cfg-defaultRootProperty'> /**
61 </span> * @cfg {String} defaultRootProperty
62 * The root property to specify on the reader if one is not explicitly defined.
64 defaultRootProperty: 'children',
66 <span id='Ext-data-TreeStore-cfg-folderSort'> /**
67 </span> * @cfg {Boolean} folderSort Set to true to automatically prepend a leaf sorter (defaults to <tt>undefined</tt>)
71 constructor: function(config) {
77 config = Ext.apply({}, config);
79 <span id='Ext-data-TreeStore-property-fields'> /**
80 </span> * If we have no fields declare for the store, add some defaults.
81 * These will be ignored if a model is explicitly specified.
83 fields = config.fields || me.fields;
85 config.fields = [{name: 'text', type: 'string'}];
88 me.callParent([config]);
90 // We create our data tree.
91 me.tree = Ext.create('Ext.data.Tree');
95 remove: me.onNodeRemove,
96 beforeexpand: me.onBeforeNodeExpand,
97 beforecollapse: me.onBeforeNodeCollapse,
98 append: me.onNodeAdded,
99 insert: me.onNodeAdded
107 me.setRootNode(root);
110 me.relayEvents(me.tree, [
111 <span id='Ext-data-TreeStore-event-append'> /**
112 </span> * @event append
113 * Fires when a new child node is appended to a node in this store's tree.
114 * @param {Tree} tree The owner tree
115 * @param {Node} parent The parent node
116 * @param {Node} node The newly appended node
117 * @param {Number} index The index of the newly appended node
121 <span id='Ext-data-TreeStore-event-remove'> /**
122 </span> * @event remove
123 * Fires when a child node is removed from a node in this store's tree.
124 * @param {Tree} tree The owner tree
125 * @param {Node} parent The parent node
126 * @param {Node} node The child node removed
130 <span id='Ext-data-TreeStore-event-move'> /**
131 </span> * @event move
132 * Fires when a node is moved to a new location in the store's tree
133 * @param {Tree} tree The owner tree
134 * @param {Node} node The node moved
135 * @param {Node} oldParent The old parent of this node
136 * @param {Node} newParent The new parent of this node
137 * @param {Number} index The index it was moved to
141 <span id='Ext-data-TreeStore-event-insert'> /**
142 </span> * @event insert
143 * Fires when a new child node is inserted in a node in this store's tree.
144 * @param {Tree} tree The owner tree
145 * @param {Node} parent The parent node
146 * @param {Node} node The child node inserted
147 * @param {Node} refNode The child node the node was inserted before
151 <span id='Ext-data-TreeStore-event-beforeappend'> /**
152 </span> * @event beforeappend
153 * Fires before a new child is appended to a node in this store's tree, return false to cancel the append.
154 * @param {Tree} tree The owner tree
155 * @param {Node} parent The parent node
156 * @param {Node} node The child node to be appended
158 "beforeappend",
160 <span id='Ext-data-TreeStore-event-beforeremove'> /**
161 </span> * @event beforeremove
162 * Fires before a child is removed from a node in this store's tree, return false to cancel the remove.
163 * @param {Tree} tree The owner tree
164 * @param {Node} parent The parent node
165 * @param {Node} node The child node to be removed
167 "beforeremove",
169 <span id='Ext-data-TreeStore-event-beforemove'> /**
170 </span> * @event beforemove
171 * Fires before a node is moved to a new location in the store's tree. Return false to cancel the move.
172 * @param {Tree} tree The owner tree
173 * @param {Node} node The node being moved
174 * @param {Node} oldParent The parent of the node
175 * @param {Node} newParent The new parent the node is moving to
176 * @param {Number} index The index it is being moved to
178 "beforemove",
180 <span id='Ext-data-TreeStore-event-beforeinsert'> /**
181 </span> * @event beforeinsert
182 * Fires before a new child is inserted in a node in this store's tree, return false to cancel the insert.
183 * @param {Tree} tree The owner tree
184 * @param {Node} parent The parent node
185 * @param {Node} node The child node to be inserted
186 * @param {Node} refNode The child node the node is being inserted before
188 "beforeinsert",
190 <span id='Ext-data-TreeStore-event-expand'> /**
191 </span> * @event expand
192 * Fires when this node is expanded.
193 * @param {Node} this The expanding node
197 <span id='Ext-data-TreeStore-event-collapse'> /**
198 </span> * @event collapse
199 * Fires when this node is collapsed.
200 * @param {Node} this The collapsing node
202 "collapse",
204 <span id='Ext-data-TreeStore-event-beforeexpand'> /**
205 </span> * @event beforeexpand
206 * Fires before this node is expanded.
207 * @param {Node} this The expanding node
209 "beforeexpand",
211 <span id='Ext-data-TreeStore-event-beforecollapse'> /**
212 </span> * @event beforecollapse
213 * Fires before this node is collapsed.
214 * @param {Node} this The collapsing node
216 "beforecollapse",
218 <span id='Ext-data-TreeStore-event-sort'> /**
219 </span> * @event sort
220 * Fires when this TreeStore is sorted.
221 * @param {Node} node The node that is sorted.
225 <span id='Ext-data-TreeStore-event-rootchange'> /**
226 </span> * @event rootchange
227 * Fires whenever the root node is changed in the tree.
228 * @param {Ext.data.Model} root The new root
230 "rootchange"
234 <span id='Ext-data-TreeStore-event-rootchange'> /**
235 </span> * @event rootchange
236 * Fires when the root node on this TreeStore is changed.
237 * @param {Ext.data.TreeStore} store This TreeStore
238 * @param {Node} The new root node.
243 //<deprecated since=0.99>
244 if (Ext.isDefined(me.nodeParameter)) {
245 if (Ext.isDefined(Ext.global.console)) {
246 Ext.global.console.warn('Ext.data.TreeStore: nodeParameter has been deprecated. Please use nodeParam instead.');
248 me.nodeParam = me.nodeParameter;
249 delete me.nodeParameter;
251 //</deprecated>
255 setProxy: function(proxy) {
259 if (proxy instanceof Ext.data.proxy.Proxy) {
260 // proxy instance, check if a root was set
261 needsRoot = Ext.isEmpty(proxy.getReader().root);
262 } else if (Ext.isString(proxy)) {
263 // string type, means a reader can't be set
266 // object, check if a reader and a root were specified.
267 reader = proxy.reader;
268 needsRoot = !(reader && !Ext.isEmpty(reader.root));
270 proxy = this.callParent(arguments);
272 reader = proxy.getReader();
273 reader.root = this.defaultRootProperty;
275 reader.buildExtractors(true);
280 onBeforeSort: function() {
281 if (this.folderSort) {
285 }, 'prepend', false);
289 <span id='Ext-data-TreeStore-method-onBeforeNodeExpand'> /**
290 </span> * Called before a node is expanded.
292 * @param {Ext.data.NodeInterface} node The node being expanded.
293 * @param {Function} callback The function to run after the expand finishes
294 * @param {Object} scope The scope in which to run the callback function
296 onBeforeNodeExpand: function(node, callback, scope) {
297 if (node.isLoaded()) {
298 Ext.callback(callback, scope || node, [node.childNodes]);
300 else if (node.isLoading()) {
301 this.on('load', function() {
302 Ext.callback(callback, scope || node, [node.childNodes]);
303 }, this, {single: true});
308 callback: function() {
309 Ext.callback(callback, scope || node, [node.childNodes]);
316 getNewRecords: function() {
317 return Ext.Array.filter(this.tree.flatten(), this.filterNew);
321 getUpdatedRecords: function() {
322 return Ext.Array.filter(this.tree.flatten(), this.filterUpdated);
325 <span id='Ext-data-TreeStore-method-onBeforeNodeCollapse'> /**
326 </span> * Called before a node is collapsed.
328 * @param {Ext.data.NodeInterface} node The node being collapsed.
329 * @param {Function} callback The function to run after the collapse finishes
330 * @param {Object} scope The scope in which to run the callback function
332 onBeforeNodeCollapse: function(node, callback, scope) {
333 callback.call(scope || node, node.childNodes);
336 onNodeRemove: function(parent, node) {
337 var removed = this.removed;
339 if (!node.isReplace && Ext.Array.indexOf(removed, node) == -1) {
344 onNodeAdded: function(parent, node) {
345 var proxy = this.getProxy(),
346 reader = proxy.getReader(),
347 data = node.raw || node.data,
350 Ext.Array.remove(this.removed, node);
352 if (!node.isLeaf() && !node.isLoaded()) {
353 dataRoot = reader.getRoot(data);
355 this.fillNode(node, reader.extractData(dataRoot));
356 delete data[reader.root];
361 <span id='Ext-data-TreeStore-method-setRootNode'> /**
362 </span> * Sets the root node for this store
363 * @param {Ext.data.Model/Ext.data.NodeInterface} root
364 * @return {Ext.data.NodeInterface} The new root
366 setRootNode: function(root) {
371 // create a default rootNode and create internal data struct.
373 id: me.defaultRootId,
377 root = Ext.ModelManager.create(root, me.model);
379 Ext.data.NodeInterface.decorate(root);
381 // Because we have decorated the model with new fields,
382 // we need to build new extactor functions on the reader.
383 me.getProxy().getReader().buildExtractors(true);
385 // When we add the root to the tree, it will automaticaly get the NodeInterface
386 me.tree.setRootNode(root);
388 // If the user has set expanded: true on the root, we want to call the expand function
389 if (!root.isLoaded() && root.isExpanded()) {
398 <span id='Ext-data-TreeStore-method-getRootNode'> /**
399 </span> * Returns the root node for this tree.
400 * @return {Ext.data.NodeInterface}
402 getRootNode: function() {
403 return this.tree.getRootNode();
406 <span id='Ext-data-TreeStore-method-getNodeById'> /**
407 </span> * Returns the record node by id
408 * @return {Ext.data.NodeInterface}
410 getNodeById: function(id) {
411 return this.tree.getNodeById(id);
414 <span id='Ext-data-TreeStore-method-load'> /**
415 </span> * Loads the Store using its configured {@link #proxy}.
416 * @param {Object} options Optional config object. This is passed into the {@link Ext.data.Operation Operation}
417 * object that is created and then sent to the proxy's {@link Ext.data.proxy.Proxy#read} function.
418 * The options can also contain a node, which indicates which node is to be loaded. If not specified, it will
419 * default to the root node.
421 load: function(options) {
422 options = options || {};
423 options.params = options.params || {};
426 node = options.node || me.tree.getRootNode(),
429 // If there is not a node it means the user hasnt defined a rootnode yet. In this case lets just
430 // create one for them.
432 node = me.setRootNode({
437 if (me.clearOnLoad) {
441 Ext.applyIf(options, {
444 options.params[me.nodeParam] = node ? node.getId() : 'root';
447 node.set('loading', true);
450 return me.callParent([options]);
454 <span id='Ext-data-TreeStore-method-fillNode'> /**
455 </span> * Fills a node with a series of child records.
457 * @param {Ext.data.NodeInterface} node The node to fill
458 * @param {Array} records The records to add
460 fillNode: function(node, records) {
462 ln = records ? records.length : 0,
463 i = 0, sortCollection;
465 if (ln && me.sortOnLoad && !me.remoteSort && me.sorters && me.sorters.items) {
466 sortCollection = Ext.create('Ext.util.MixedCollection');
467 sortCollection.addAll(records);
468 sortCollection.sort(me.sorters.items);
469 records = sortCollection.items;
472 node.set('loaded', true);
473 for (; i < ln; i++) {
474 node.appendChild(records[i], undefined, true);
481 onProxyLoad: function(operation) {
483 successful = operation.wasSuccessful(),
484 records = operation.getRecords(),
485 node = operation.node;
487 node.set('loading', false);
489 records = me.fillNode(node, records);
492 me.fireEvent('read', me, operation.node, records, successful);
493 me.fireEvent('load', me, operation.node, records, successful);
494 //this is a callback that would have been passed to the 'read' function and is optional
495 Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
498 <span id='Ext-data-TreeStore-method-onCreateRecords'> /**
499 </span> * Create any new records when a write is returned from the server.
501 * @param {Array} records The array of new records
502 * @param {Ext.data.Operation} operation The operation that just completed
503 * @param {Boolean} success True if the operation was successful
505 onCreateRecords: function(records, operation, success) {
508 length = records.length,
509 originalRecords = operation.records,
516 * Loop over each record returned from the server. Assume they are
517 * returned in order of how they were sent. If we find a matching
518 * record, replace it with the newly created one.
520 for (; i < length; ++i) {
522 original = originalRecords[i];
524 parentNode = original.parentNode;
526 // prevent being added to the removed cache
527 original.isReplace = true;
528 parentNode.replaceChild(record, original);
529 delete original.isReplace;
531 record.phantom = false;
537 <span id='Ext-data-TreeStore-method-onUpdateRecords'> /**
538 </span> * Update any records when a write is returned from the server.
540 * @param {Array} records The array of updated records
541 * @param {Ext.data.Operation} operation The operation that just completed
542 * @param {Boolean} success True if the operation was successful
544 onUpdateRecords: function(records, operation, success){
548 length = records.length,
554 for (; i < length; ++i) {
556 original = me.tree.getNodeById(record.getId());
557 parentNode = original.parentNode;
559 // prevent being added to the removed cache
560 original.isReplace = true;
561 parentNode.replaceChild(record, original);
562 original.isReplace = false;
568 <span id='Ext-data-TreeStore-method-onDestroyRecords'> /**
569 </span> * Remove any records when a write is returned from the server.
571 * @param {Array} records The array of removed records
572 * @param {Ext.data.Operation} operation The operation that just completed
573 * @param {Boolean} success True if the operation was successful
575 onDestroyRecords: function(records, operation, success){
582 removeAll: function() {
583 this.getRootNode().destroy(true);
584 this.fireEvent('clear', this);
588 doSort: function(sorterFn) {
591 //the load function will pick up the new sorters and request the sorted data from the proxy
594 me.tree.sort(sorterFn, true);
595 me.fireEvent('datachanged', me);
597 me.fireEvent('sort', me);