4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../resources/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. These fields are listed
29 * in the {@link Ext.data.NodeInterface} documentation.
31 * # Reading Nested Data
33 * For the tree to read nested data, the {@link Ext.data.reader.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-root'> /**
43 </span> * @cfg {Ext.data.Model/Ext.data.NodeInterface/Object} root
44 * The root node for this store. For example:
48 * text: "My Root",
50 * { text: "Child 1", leaf: true },
51 * { text: "Child 2", expanded: true, children: [
52 * { text: "GrandChild", leaf: true }
57 * Setting the `root` config option is the same as calling {@link #setRootNode}.
60 <span id='Ext-data-TreeStore-cfg-clearOnLoad'> /**
61 </span> * @cfg {Boolean} clearOnLoad
62 * Remove previously existing child nodes before loading. Default to true.
66 <span id='Ext-data-TreeStore-cfg-nodeParam'> /**
67 </span> * @cfg {String} nodeParam
68 * The name of the parameter sent to the server which contains the identifier of the node.
73 <span id='Ext-data-TreeStore-cfg-defaultRootId'> /**
74 </span> * @cfg {String} defaultRootId
75 * The default root id. Defaults to 'root'
77 defaultRootId: 'root',
79 <span id='Ext-data-TreeStore-cfg-defaultRootProperty'> /**
80 </span> * @cfg {String} defaultRootProperty
81 * The root property to specify on the reader if one is not explicitly defined.
83 defaultRootProperty: 'children',
85 <span id='Ext-data-TreeStore-cfg-folderSort'> /**
86 </span> * @cfg {Boolean} folderSort
87 * Set to true to automatically prepend a leaf sorter. Defaults to `undefined`.
91 constructor: function(config) {
96 config = Ext.apply({}, config);
98 <span id='Ext-data-TreeStore-property-fields'> /**
99 </span> * If we have no fields declare for the store, add some defaults.
100 * These will be ignored if a model is explicitly specified.
102 fields = config.fields || me.fields;
104 config.fields = [{name: 'text', type: 'string'}];
107 me.callParent([config]);
109 // We create our data tree.
110 me.tree = Ext.create('Ext.data.Tree');
112 me.relayEvents(me.tree, [
113 <span id='Ext-data-TreeStore-event-append'> /**
114 </span> * @event append
115 * @alias Ext.data.Tree#append
119 <span id='Ext-data-TreeStore-event-remove'> /**
120 </span> * @event remove
121 * @alias Ext.data.Tree#remove
125 <span id='Ext-data-TreeStore-event-move'> /**
126 </span> * @event move
127 * @alias Ext.data.Tree#move
131 <span id='Ext-data-TreeStore-event-insert'> /**
132 </span> * @event insert
133 * @alias Ext.data.Tree#insert
137 <span id='Ext-data-TreeStore-event-beforeappend'> /**
138 </span> * @event beforeappend
139 * @alias Ext.data.Tree#beforeappend
141 "beforeappend",
143 <span id='Ext-data-TreeStore-event-beforeremove'> /**
144 </span> * @event beforeremove
145 * @alias Ext.data.Tree#beforeremove
147 "beforeremove",
149 <span id='Ext-data-TreeStore-event-beforemove'> /**
150 </span> * @event beforemove
151 * @alias Ext.data.Tree#beforemove
153 "beforemove",
155 <span id='Ext-data-TreeStore-event-beforeinsert'> /**
156 </span> * @event beforeinsert
157 * @alias Ext.data.Tree#beforeinsert
159 "beforeinsert",
161 <span id='Ext-data-TreeStore-event-expand'> /**
162 </span> * @event expand
163 * @alias Ext.data.Tree#expand
167 <span id='Ext-data-TreeStore-event-collapse'> /**
168 </span> * @event collapse
169 * @alias Ext.data.Tree#collapse
171 "collapse",
173 <span id='Ext-data-TreeStore-event-beforeexpand'> /**
174 </span> * @event beforeexpand
175 * @alias Ext.data.Tree#beforeexpand
177 "beforeexpand",
179 <span id='Ext-data-TreeStore-event-beforecollapse'> /**
180 </span> * @event beforecollapse
181 * @alias Ext.data.Tree#beforecollapse
183 "beforecollapse",
185 <span id='Ext-data-TreeStore-event-rootchange'> /**
186 </span> * @event rootchange
187 * @alias Ext.data.Tree#rootchange
189 "rootchange"
194 remove: me.onNodeRemove,
195 // this event must follow the relay to beforeitemexpand to allow users to
196 // cancel the expand:
197 beforeexpand: me.onBeforeNodeExpand,
198 beforecollapse: me.onBeforeNodeCollapse,
199 append: me.onNodeAdded,
200 insert: me.onNodeAdded
208 me.setRootNode(root);
212 <span id='Ext-data-TreeStore-event-sort'> /**
213 </span> * @event sort
214 * Fires when this TreeStore is sorted.
215 * @param {Ext.data.NodeInterface} node The node that is sorted.
220 //<deprecated since=0.99>
221 if (Ext.isDefined(me.nodeParameter)) {
222 if (Ext.isDefined(Ext.global.console)) {
223 Ext.global.console.warn('Ext.data.TreeStore: nodeParameter has been deprecated. Please use nodeParam instead.');
225 me.nodeParam = me.nodeParameter;
226 delete me.nodeParameter;
228 //</deprecated>
232 setProxy: function(proxy) {
236 if (proxy instanceof Ext.data.proxy.Proxy) {
237 // proxy instance, check if a root was set
238 needsRoot = Ext.isEmpty(proxy.getReader().root);
239 } else if (Ext.isString(proxy)) {
240 // string type, means a reader can't be set
243 // object, check if a reader and a root were specified.
244 reader = proxy.reader;
245 needsRoot = !(reader && !Ext.isEmpty(reader.root));
247 proxy = this.callParent(arguments);
249 reader = proxy.getReader();
250 reader.root = this.defaultRootProperty;
252 reader.buildExtractors(true);
257 onBeforeSort: function() {
258 if (this.folderSort) {
262 }, 'prepend', false);
266 <span id='Ext-data-TreeStore-method-onBeforeNodeExpand'> /**
267 </span> * Called before a node is expanded.
269 * @param {Ext.data.NodeInterface} node The node being expanded.
270 * @param {Function} callback The function to run after the expand finishes
271 * @param {Object} scope The scope in which to run the callback function
273 onBeforeNodeExpand: function(node, callback, scope) {
274 if (node.isLoaded()) {
275 Ext.callback(callback, scope || node, [node.childNodes]);
277 else if (node.isLoading()) {
278 this.on('load', function() {
279 Ext.callback(callback, scope || node, [node.childNodes]);
280 }, this, {single: true});
285 callback: function() {
286 Ext.callback(callback, scope || node, [node.childNodes]);
293 getNewRecords: function() {
294 return Ext.Array.filter(this.tree.flatten(), this.filterNew);
298 getUpdatedRecords: function() {
299 return Ext.Array.filter(this.tree.flatten(), this.filterUpdated);
302 <span id='Ext-data-TreeStore-method-onBeforeNodeCollapse'> /**
303 </span> * Called before a node is collapsed.
305 * @param {Ext.data.NodeInterface} node The node being collapsed.
306 * @param {Function} callback The function to run after the collapse finishes
307 * @param {Object} scope The scope in which to run the callback function
309 onBeforeNodeCollapse: function(node, callback, scope) {
310 callback.call(scope || node, node.childNodes);
313 onNodeRemove: function(parent, node) {
314 var removed = this.removed;
316 if (!node.isReplace && Ext.Array.indexOf(removed, node) == -1) {
321 onNodeAdded: function(parent, node) {
322 var proxy = this.getProxy(),
323 reader = proxy.getReader(),
324 data = node.raw || node.data,
327 Ext.Array.remove(this.removed, node);
329 if (!node.isLeaf() && !node.isLoaded()) {
330 dataRoot = reader.getRoot(data);
332 this.fillNode(node, reader.extractData(dataRoot));
333 delete data[reader.root];
338 <span id='Ext-data-TreeStore-method-setRootNode'> /**
339 </span> * Sets the root node for this store. See also the {@link #root} config option.
340 * @param {Ext.data.Model/Ext.data.NodeInterface/Object} root
341 * @return {Ext.data.NodeInterface} The new root
343 setRootNode: function(root) {
348 // create a default rootNode and create internal data struct.
350 id: me.defaultRootId,
354 root = Ext.ModelManager.create(root, me.model);
356 Ext.data.NodeInterface.decorate(root);
358 // Because we have decorated the model with new fields,
359 // we need to build new extactor functions on the reader.
360 me.getProxy().getReader().buildExtractors(true);
362 // When we add the root to the tree, it will automaticaly get the NodeInterface
363 me.tree.setRootNode(root);
365 // If the user has set expanded: true on the root, we want to call the expand function
366 if (!root.isLoaded() && (me.autoLoad === true || root.isExpanded())) {
375 <span id='Ext-data-TreeStore-method-getRootNode'> /**
376 </span> * Returns the root node for this tree.
377 * @return {Ext.data.NodeInterface}
379 getRootNode: function() {
380 return this.tree.getRootNode();
383 <span id='Ext-data-TreeStore-method-getNodeById'> /**
384 </span> * Returns the record node by id
385 * @return {Ext.data.NodeInterface}
387 getNodeById: function(id) {
388 return this.tree.getNodeById(id);
391 <span id='Ext-data-TreeStore-method-load'> /**
392 </span> * Loads the Store using its configured {@link #proxy}.
393 * @param {Object} options (Optional) config object. This is passed into the {@link Ext.data.Operation Operation}
394 * object that is created and then sent to the proxy's {@link Ext.data.proxy.Proxy#read} function.
395 * The options can also contain a node, which indicates which node is to be loaded. If not specified, it will
396 * default to the root node.
398 load: function(options) {
399 options = options || {};
400 options.params = options.params || {};
403 node = options.node || me.tree.getRootNode(),
406 // If there is not a node it means the user hasnt defined a rootnode yet. In this case lets just
407 // create one for them.
409 node = me.setRootNode({
414 if (me.clearOnLoad) {
415 node.removeAll(true);
418 Ext.applyIf(options, {
421 options.params[me.nodeParam] = node ? node.getId() : 'root';
424 node.set('loading', true);
427 return me.callParent([options]);
431 <span id='Ext-data-TreeStore-method-fillNode'> /**
432 </span> * Fills a node with a series of child records.
434 * @param {Ext.data.NodeInterface} node The node to fill
435 * @param {Ext.data.Model[]} records The records to add
437 fillNode: function(node, records) {
439 ln = records ? records.length : 0,
440 i = 0, sortCollection;
442 if (ln && me.sortOnLoad && !me.remoteSort && me.sorters && me.sorters.items) {
443 sortCollection = Ext.create('Ext.util.MixedCollection');
444 sortCollection.addAll(records);
445 sortCollection.sort(me.sorters.items);
446 records = sortCollection.items;
449 node.set('loaded', true);
450 for (; i < ln; i++) {
451 node.appendChild(records[i], undefined, true);
458 onProxyLoad: function(operation) {
460 successful = operation.wasSuccessful(),
461 records = operation.getRecords(),
462 node = operation.node;
465 node.set('loading', false);
467 records = me.fillNode(node, records);
469 // The load event has an extra node parameter
470 // (differing from the load event described in AbstractStore)
471 <span id='Ext-data-TreeStore-event-load'> /**
472 </span> * @event load
473 * Fires whenever the store reads data from a remote data source.
474 * @param {Ext.data.TreeStore} this
475 * @param {Ext.data.NodeInterface} node The node that was loaded.
476 * @param {Ext.data.Model[]} records An array of records.
477 * @param {Boolean} successful True if the operation was successful.
480 me.fireEvent('read', me, operation.node, records, successful);
481 me.fireEvent('load', me, operation.node, records, successful);
482 //this is a callback that would have been passed to the 'read' function and is optional
483 Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
486 <span id='Ext-data-TreeStore-method-onCreateRecords'> /**
487 </span> * Creates any new records when a write is returned from the server.
489 * @param {Ext.data.Model[]} records The array of new records
490 * @param {Ext.data.Operation} operation The operation that just completed
491 * @param {Boolean} success True if the operation was successful
493 onCreateRecords: function(records, operation, success) {
496 length = records.length,
497 originalRecords = operation.records,
504 * Loop over each record returned from the server. Assume they are
505 * returned in order of how they were sent. If we find a matching
506 * record, replace it with the newly created one.
508 for (; i < length; ++i) {
510 original = originalRecords[i];
512 parentNode = original.parentNode;
514 // prevent being added to the removed cache
515 original.isReplace = true;
516 parentNode.replaceChild(record, original);
517 delete original.isReplace;
519 record.phantom = false;
525 <span id='Ext-data-TreeStore-method-onUpdateRecords'> /**
526 </span> * Updates any records when a write is returned from the server.
528 * @param {Ext.data.Model[]} records The array of updated records
529 * @param {Ext.data.Operation} operation The operation that just completed
530 * @param {Boolean} success True if the operation was successful
532 onUpdateRecords: function(records, operation, success){
536 length = records.length,
542 for (; i < length; ++i) {
544 original = me.tree.getNodeById(record.getId());
545 parentNode = original.parentNode;
547 // prevent being added to the removed cache
548 original.isReplace = true;
549 parentNode.replaceChild(record, original);
550 original.isReplace = false;
556 <span id='Ext-data-TreeStore-method-onDestroyRecords'> /**
557 </span> * Removes any records when a write is returned from the server.
559 * @param {Ext.data.Model[]} records The array of removed records
560 * @param {Ext.data.Operation} operation The operation that just completed
561 * @param {Boolean} success True if the operation was successful
563 onDestroyRecords: function(records, operation, success){
570 removeAll: function() {
571 this.getRootNode().destroy(true);
572 this.fireEvent('clear', this);
576 doSort: function(sorterFn) {
579 //the load function will pick up the new sorters and request the sorted data from the proxy
582 me.tree.sort(sorterFn, true);
583 me.fireEvent('datachanged', me);
585 me.fireEvent('sort', me);