X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/530ef4b6c5b943cfa68b779d11cf7de29aa878bf..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/docs/source/TreeStore.html diff --git a/docs/source/TreeStore.html b/docs/source/TreeStore.html new file mode 100644 index 00000000..40f360f4 --- /dev/null +++ b/docs/source/TreeStore.html @@ -0,0 +1,582 @@ +Sencha Documentation Project
/**
+ * @class Ext.data.TreeStore
+ * @extends Ext.data.AbstractStore
+ * 
+ * The TreeStore is a store implementation that is backed by by an {@link Ext.data.Tree}.
+ * It provides convenience methods for loading nodes, as well as the ability to use
+ * the hierarchical tree structure combined with a store. This class is generally used
+ * in conjunction with {@link Ext.tree.Panel}. This class also relays many events from
+ * the Tree for convenience.
+ * 
+ * ## Using Models
+ * If no Model is specified, an implicit model will be created that implements {@link Ext.data.NodeInterface}.
+ * The standard Tree fields will also be copied onto the Model for maintaining their state.
+ * 
+ * ## Reading Nested Data
+ * For the tree to read nested data, the {@link Ext.data.Reader} must be configured with a root property,
+ * so the reader can find nested data for each node. If a root is not specified, it will default to
+ * 'children'.
+ */
+Ext.define('Ext.data.TreeStore', {
+    extend: 'Ext.data.AbstractStore',
+    alias: 'store.tree',
+    requires: ['Ext.data.Tree', 'Ext.data.NodeInterface', 'Ext.data.NodeStore'],
+
+    /**
+     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
+     * child nodes before loading.
+     */
+    clearOnLoad : true,
+
+    /**
+     * @cfg {String} nodeParam The name of the parameter sent to the server which contains
+     * the identifier of the node. Defaults to <tt>'node'</tt>.
+     */
+    nodeParam: 'node',
+
+    /**
+     * @cfg {String} defaultRootId
+     * The default root id. Defaults to 'root'
+     */
+    defaultRootId: 'root',
+    
+    /**
+     * @cfg {String} defaultRootProperty
+     * The root property to specify on the reader if one is not explicitly defined.
+     */
+    defaultRootProperty: 'children',
+
+    /**
+     * @cfg {Boolean} folderSort Set to true to automatically prepend a leaf sorter (defaults to <tt>undefined</tt>)
+     */
+    folderSort: false,
+    
+    constructor: function(config) {
+        var me = this, 
+            root,
+            fields;
+            
+        
+        config = Ext.apply({}, config);
+        
+        /**
+         * If we have no fields declare for the store, add some defaults.
+         * These will be ignored if a model is explicitly specified.
+         */
+        fields = config.fields || me.fields;
+        if (!fields) {
+            config.fields = [{name: 'text', type: 'string'}];
+        }
+
+        me.callParent([config]);
+        
+        // We create our data tree.
+        me.tree = Ext.create('Ext.data.Tree');
+        
+        me.tree.on({
+            scope: me,
+            remove: me.onNodeRemove,
+            beforeexpand: me.onBeforeNodeExpand,
+            beforecollapse: me.onBeforeNodeCollapse,
+            append: me.onNodeAdded,
+            insert: me.onNodeAdded
+        });
+
+        me.onBeforeSort();
+                
+        root = me.root;
+        if (root) {
+            delete me.root;
+            me.setRootNode(root);            
+        }
+
+        me.relayEvents(me.tree, [
+            /**
+             * @event append
+             * Fires when a new child node is appended to a node in this store's tree.
+             * @param {Tree} tree The owner tree
+             * @param {Node} parent The parent node
+             * @param {Node} node The newly appended node
+             * @param {Number} index The index of the newly appended node
+             */
+            "append",
+            
+            /**
+             * @event remove
+             * Fires when a child node is removed from a node in this store's tree.
+             * @param {Tree} tree The owner tree
+             * @param {Node} parent The parent node
+             * @param {Node} node The child node removed
+             */
+            "remove",
+            
+            /**
+             * @event move
+             * Fires when a node is moved to a new location in the store's tree
+             * @param {Tree} tree The owner tree
+             * @param {Node} node The node moved
+             * @param {Node} oldParent The old parent of this node
+             * @param {Node} newParent The new parent of this node
+             * @param {Number} index The index it was moved to
+             */
+            "move",
+            
+            /**
+             * @event insert
+             * Fires when a new child node is inserted in a node in this store's tree.
+             * @param {Tree} tree The owner tree
+             * @param {Node} parent The parent node
+             * @param {Node} node The child node inserted
+             * @param {Node} refNode The child node the node was inserted before
+             */
+            "insert",
+            
+            /**
+             * @event beforeappend
+             * Fires before a new child is appended to a node in this store's tree, return false to cancel the append.
+             * @param {Tree} tree The owner tree
+             * @param {Node} parent The parent node
+             * @param {Node} node The child node to be appended
+             */
+            "beforeappend",
+            
+            /**
+             * @event beforeremove
+             * Fires before a child is removed from a node in this store's tree, return false to cancel the remove.
+             * @param {Tree} tree The owner tree
+             * @param {Node} parent The parent node
+             * @param {Node} node The child node to be removed
+             */
+            "beforeremove",
+            
+            /**
+             * @event beforemove
+             * Fires before a node is moved to a new location in the store's tree. Return false to cancel the move.
+             * @param {Tree} tree The owner tree
+             * @param {Node} node The node being moved
+             * @param {Node} oldParent The parent of the node
+             * @param {Node} newParent The new parent the node is moving to
+             * @param {Number} index The index it is being moved to
+             */
+            "beforemove",
+            
+            /**
+             * @event beforeinsert
+             * Fires before a new child is inserted in a node in this store's tree, return false to cancel the insert.
+             * @param {Tree} tree The owner tree
+             * @param {Node} parent The parent node
+             * @param {Node} node The child node to be inserted
+             * @param {Node} refNode The child node the node is being inserted before
+             */
+            "beforeinsert",
+             
+             /**
+              * @event expand
+              * Fires when this node is expanded.
+              * @param {Node} this The expanding node
+              */
+             "expand",
+             
+             /**
+              * @event collapse
+              * Fires when this node is collapsed.
+              * @param {Node} this The collapsing node
+              */
+             "collapse",
+             
+             /**
+              * @event beforeexpand
+              * Fires before this node is expanded.
+              * @param {Node} this The expanding node
+              */
+             "beforeexpand",
+             
+             /**
+              * @event beforecollapse
+              * Fires before this node is collapsed.
+              * @param {Node} this The collapsing node
+              */
+             "beforecollapse",
+
+             /**
+              * @event sort
+              * Fires when this TreeStore is sorted.
+              * @param {Node} node The node that is sorted.
+              */             
+             "sort",
+             
+             /**
+              * @event rootchange
+              * Fires whenever the root node is changed in the tree.
+              * @param {Ext.data.Model} root The new root
+              */
+             "rootchange"
+        ]);
+        
+        me.addEvents(
+            /**
+             * @event rootchange
+             * Fires when the root node on this TreeStore is changed.
+             * @param {Ext.data.TreeStore} store This TreeStore
+             * @param {Node} The new root node.
+             */
+            'rootchange'
+        );
+        
+        //<deprecated since=0.99>
+        if (Ext.isDefined(me.nodeParameter)) {
+            if (Ext.isDefined(Ext.global.console)) {
+                Ext.global.console.warn('Ext.data.TreeStore: nodeParameter has been deprecated. Please use nodeParam instead.');
+            }
+            me.nodeParam = me.nodeParameter;
+            delete me.nodeParameter;
+        }
+        //</deprecated>
+    },
+    
+    // inherit docs
+    setProxy: function(proxy) {
+        var reader,
+            needsRoot;
+        
+        if (proxy instanceof Ext.data.proxy.Proxy) {
+            // proxy instance, check if a root was set
+            needsRoot = Ext.isEmpty(proxy.getReader().root);
+        } else if (Ext.isString(proxy)) {
+            // string type, means a reader can't be set
+            needsRoot = true;
+        } else {
+            // object, check if a reader and a root were specified.
+            reader = proxy.reader;
+            needsRoot = !(reader && !Ext.isEmpty(reader.root));
+        }
+        proxy = this.callParent(arguments);
+        if (needsRoot) {
+            reader = proxy.getReader();
+            reader.root = this.defaultRootProperty;
+            // force rebuild
+            reader.buildExtractors(true);
+        }
+    },
+    
+    // inherit docs
+    onBeforeSort: function() {
+        if (this.folderSort) {
+            this.sort({
+                property: 'leaf',
+                direction: 'ASC'
+            }, 'prepend', false);    
+        }
+    },
+    
+    /**
+     * Called before a node is expanded.
+     * @private
+     * @param {Ext.data.NodeInterface} node The node being expanded.
+     * @param {Function} callback The function to run after the expand finishes
+     * @param {Object} scope The scope in which to run the callback function
+     */
+    onBeforeNodeExpand: function(node, callback, scope) {
+        if (node.isLoaded()) {
+            Ext.callback(callback, scope || node, [node.childNodes]);
+        }
+        else if (node.isLoading()) {
+            this.on('load', function() {
+                Ext.callback(callback, scope || node, [node.childNodes]);
+            }, this, {single: true});
+        }
+        else {
+            this.read({
+                node: node,
+                callback: function() {
+                    Ext.callback(callback, scope || node, [node.childNodes]);
+                }
+            });            
+        }
+    },
+    
+    //inherit docs
+    getNewRecords: function() {
+        return Ext.Array.filter(this.tree.flatten(), this.filterNew);
+    },
+
+    //inherit docs
+    getUpdatedRecords: function() {
+        return Ext.Array.filter(this.tree.flatten(), this.filterUpdated);
+    },
+    
+    /**
+     * Called before a node is collapsed.
+     * @private
+     * @param {Ext.data.NodeInterface} node The node being collapsed.
+     * @param {Function} callback The function to run after the collapse finishes
+     * @param {Object} scope The scope in which to run the callback function
+     */
+    onBeforeNodeCollapse: function(node, callback, scope) {
+        callback.call(scope || node, node.childNodes);
+    },
+    
+    onNodeRemove: function(parent, node) {
+        var removed = this.removed;
+        
+        if (!node.isReplace && Ext.Array.indexOf(removed, node) == -1) {
+            removed.push(node);
+        }
+    },
+    
+    onNodeAdded: function(parent, node) {
+        var proxy = this.getProxy(),
+            reader = proxy.getReader(),
+            data = node.raw || node.data,
+            dataRoot, children;
+            
+        Ext.Array.remove(this.removed, node); 
+        
+        if (!node.isLeaf() && !node.isLoaded()) {
+            dataRoot = reader.getRoot(data);
+            if (dataRoot) {
+                this.fillNode(node, reader.extractData(dataRoot));
+                delete data[reader.root];
+            }
+        }
+    },
+        
+    /**
+     * Sets the root node for this store
+     * @param {Ext.data.Model/Ext.data.NodeInterface} root
+     * @return {Ext.data.NodeInterface} The new root
+     */
+    setRootNode: function(root) {
+        var me = this;
+
+        root = root || {};        
+        if (!root.isNode) {
+            // create a default rootNode and create internal data struct.        
+            Ext.applyIf(root, {
+                id: me.defaultRootId,
+                text: 'Root',
+                allowDrag: false
+            });
+            root = Ext.ModelManager.create(root, me.model);
+        }
+        Ext.data.NodeInterface.decorate(root);
+
+        // Because we have decorated the model with new fields,
+        // we need to build new extactor functions on the reader.
+        me.getProxy().getReader().buildExtractors(true);
+        
+        // When we add the root to the tree, it will automaticaly get the NodeInterface
+        me.tree.setRootNode(root);
+        
+        // If the user has set expanded: true on the root, we want to call the expand function
+        if (!root.isLoaded() && root.isExpanded()) {
+            me.load({
+                node: root
+            });
+        }
+        
+        return root;
+    },
+        
+    /**
+     * Returns the root node for this tree.
+     * @return {Ext.data.NodeInterface}
+     */
+    getRootNode: function() {
+        return this.tree.getRootNode();
+    },
+
+    /**
+     * Returns the record node by id
+     * @return {Ext.data.NodeInterface}
+     */
+    getNodeById: function(id) {
+        return this.tree.getNodeById(id);
+    },
+
+    /**
+     * Loads the Store using its configured {@link #proxy}.
+     * @param {Object} options Optional config object. This is passed into the {@link Ext.data.Operation Operation}
+     * object that is created and then sent to the proxy's {@link Ext.data.proxy.Proxy#read} function.
+     * The options can also contain a node, which indicates which node is to be loaded. If not specified, it will
+     * default to the root node.
+     */
+    load: function(options) {
+        options = options || {};
+        options.params = options.params || {};
+        
+        var me = this,
+            node = options.node || me.tree.getRootNode(),
+            root;
+            
+        // If there is not a node it means the user hasnt defined a rootnode yet. In this case lets just
+        // create one for them.
+        if (!node) {
+            node = me.setRootNode({
+                expanded: true
+            });
+        }
+        
+        if (me.clearOnLoad) {
+            node.removeAll();
+        }
+        
+        Ext.applyIf(options, {
+            node: node
+        });
+        options.params[me.nodeParam] = node ? node.getId() : 'root';
+        
+        if (node) {
+            node.set('loading', true);
+        }
+        
+        return me.callParent([options]);
+    },
+        
+
+    /**
+     * Fills a node with a series of child records.
+     * @private
+     * @param {Ext.data.NodeInterface} node The node to fill
+     * @param {Array} records The records to add
+     */
+    fillNode: function(node, records) {
+        var me = this,
+            ln = records ? records.length : 0,
+            i = 0, sortCollection;
+
+        if (ln && me.sortOnLoad && !me.remoteSort && me.sorters && me.sorters.items) {
+            sortCollection = Ext.create('Ext.util.MixedCollection');
+            sortCollection.addAll(records);
+            sortCollection.sort(me.sorters.items);
+            records = sortCollection.items;
+        }
+        
+        node.set('loaded', true);
+        for (; i < ln; i++) {
+            node.appendChild(records[i], undefined, true);
+        }
+        
+        return records;
+    },
+
+    // inherit docs
+    onProxyLoad: function(operation) {
+        var me = this,
+            successful = operation.wasSuccessful(),
+            records = operation.getRecords(),
+            node = operation.node;
+
+        node.set('loading', false);
+        if (successful) {
+            records = me.fillNode(node, records);
+        }
+        // deprecate read?
+        me.fireEvent('read', me, operation.node, records, successful);
+        me.fireEvent('load', me, operation.node, records, successful);
+        //this is a callback that would have been passed to the 'read' function and is optional
+        Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
+    },
+    
+    /**
+     * Create any new records when a write is returned from the server.
+     * @private
+     * @param {Array} records The array of new records
+     * @param {Ext.data.Operation} operation The operation that just completed
+     * @param {Boolean} success True if the operation was successful
+     */
+    onCreateRecords: function(records, operation, success) {
+        if (success) {
+            var i = 0,
+                length = records.length,
+                originalRecords = operation.records,
+                parentNode,
+                record,
+                original,
+                index;
+
+            /**
+             * Loop over each record returned from the server. Assume they are
+             * returned in order of how they were sent. If we find a matching
+             * record, replace it with the newly created one.
+             */
+            for (; i < length; ++i) {
+                record = records[i];
+                original = originalRecords[i];
+                if (original) {
+                    parentNode = original.parentNode;
+                    if (parentNode) {
+                        // prevent being added to the removed cache
+                        original.isReplace = true;
+                        parentNode.replaceChild(record, original);
+                        delete original.isReplace;
+                    }
+                    record.phantom = false;
+                }
+            }
+        }
+    },
+
+    /**
+     * Update any records when a write is returned from the server.
+     * @private
+     * @param {Array} records The array of updated records
+     * @param {Ext.data.Operation} operation The operation that just completed
+     * @param {Boolean} success True if the operation was successful
+     */
+    onUpdateRecords: function(records, operation, success){
+        if (success) {
+            var me = this,
+                i = 0,
+                length = records.length,
+                data = me.data,
+                original,
+                parentNode,
+                record;
+
+            for (; i < length; ++i) {
+                record = records[i];
+                original = me.tree.getNodeById(record.getId());
+                parentNode = original.parentNode;
+                if (parentNode) {
+                    // prevent being added to the removed cache
+                    original.isReplace = true;
+                    parentNode.replaceChild(record, original);
+                    original.isReplace = false;
+                }
+            }
+        }
+    },
+
+    /**
+     * Remove any records when a write is returned from the server.
+     * @private
+     * @param {Array} records The array of removed records
+     * @param {Ext.data.Operation} operation The operation that just completed
+     * @param {Boolean} success True if the operation was successful
+     */
+    onDestroyRecords: function(records, operation, success){
+        if (success) {
+            this.removed = [];
+        }
+    },
+
+    // inherit docs
+    removeAll: function() {
+        this.getRootNode().destroy();
+        this.fireEvent('clear', this);
+    },
+
+    // inherit docs
+    doSort: function(sorterFn) {
+        var me = this;
+        if (me.remoteSort) {
+            //the load function will pick up the new sorters and request the sorted data from the proxy
+            me.load();
+        } else {
+            me.tree.sort(sorterFn, true);
+            me.fireEvent('datachanged', me);
+        }   
+        me.fireEvent('sort', me);
+    }
+});
\ No newline at end of file