/** * @class Ext.tree.TreeLoader * @extends Ext.util.Observable * A TreeLoader provides for lazy loading of an {@link Ext.tree.TreeNode}'s child * nodes from a specified URL. The response must be a JavaScript Array definition * whose elements are node definition objects. e.g.: **[{ id: 1, text: 'A leaf Node', leaf: true },{ id: 2, text: 'A folder Node', children: [{ id: 3, text: 'A child Node', leaf: true }] }]
* A server request is sent, and child nodes are loaded only when a node is expanded. * The loading node's id is passed to the server under the parameter name "node" to * enable the server to produce the correct child nodes. *
* To pass extra parameters, an event handler may be attached to the "beforeload" * event, and the parameters specified in the TreeLoader's baseParams property: ** This would pass an HTTP parameter called "category" to the server containing * the value of the Node's "category" attribute. * @constructor * Creates a new Treeloader. * @param {Object} config A config object containing config properties. */ Ext.tree.TreeLoader = function(config){ this.baseParams = {}; Ext.apply(this, config); this.addEvents( /** * @event beforeload * Fires before a network request is made to retrieve the Json text which specifies a node's children. * @param {Object} This TreeLoader object. * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded. * @param {Object} callback The callback function specified in the {@link #load} call. */ "beforeload", /** * @event load * Fires when the node has been successfuly loaded. * @param {Object} This TreeLoader object. * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded. * @param {Object} response The response object containing the data from the server. */ "load", /** * @event loadexception * Fires if the network request failed. * @param {Object} This TreeLoader object. * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded. * @param {Object} response The response object containing the data from the server. */ "loadexception" ); Ext.tree.TreeLoader.superclass.constructor.call(this); if(typeof this.paramOrder == 'string'){ this.paramOrder = this.paramOrder.split(/[\s,|]/); } }; Ext.extend(Ext.tree.TreeLoader, Ext.util.Observable, { /** * @cfg {String} dataUrl The URL from which to request a Json string which * specifies an array of node definition objects representing the child nodes * to be loaded. */ /** * @cfg {String} requestMethod The HTTP request method for loading data (defaults to the value of {@link Ext.Ajax#method}). */ /** * @cfg {String} url Equivalent to {@link #dataUrl}. */ /** * @cfg {Boolean} preloadChildren If set to true, the loader recursively loads "children" attributes when doing the first load on nodes. */ /** * @cfg {Object} baseParams (optional) An object containing properties which * specify HTTP parameters to be passed to each request for child nodes. */ /** * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes * created by this loader. If the attributes sent by the server have an attribute in this object, * they take priority. */ /** * @cfg {Object} uiProviders (optional) An object containing properties which * specify custom {@link Ext.tree.TreeNodeUI} implementations. If the optional * uiProvider attribute of a returned child node is a string rather * than a reference to a TreeNodeUI implementation, then that string value * is used as a property name in the uiProviders object. */ uiProviders : {}, /** * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing * child nodes before loading. */ clearOnLoad : true, /** * @cfg {Array/String} paramOrder Defaults to undefined. Only used when using directFn. * A list of params to be executed * server side. Specify the params in the order in which they must be executed on the server-side * as either (1) an Array of String values, or (2) a String of params delimited by either whitespace, * comma, or pipe. For example, * any of the following would be acceptable:myTreeLoader.on("beforeload", function(treeLoader, node) { this.baseParams.category = node.attributes.category; }, this);
*/ paramOrder: undefined, /** * @cfg {Boolean} paramsAsHash Only used when using directFn. * Send parameters as a collection of named arguments (defaults to false). Providing a * {@link #paramOrder} nullifies this configuration. */ paramsAsHash: false, /** * @cfg {Function} directFn * Function to call when executing a request. */ directFn : undefined, /** * Load an {@link Ext.tree.TreeNode} from the URL specified in the constructor. * This is called automatically when a node is expanded, but may be used to reload * a node (or append new children if the {@link #clearOnLoad} option is false.) * @param {Ext.tree.TreeNode} node * @param {Function} callback * @param (Object) scope */ load : function(node, callback, scope){ if(this.clearOnLoad){ while(node.firstChild){ node.removeChild(node.firstChild); } } if(this.doPreload(node)){ // preloaded json children this.runCallback(callback, scope || node, []); }else if(this.directFn || this.dataUrl || this.url){ this.requestData(node, callback, scope || node); } }, doPreload : function(node){ if(node.attributes.children){ if(node.childNodes.length < 1){ // preloaded? var cs = node.attributes.children; node.beginUpdate(); for(var i = 0, len = cs.length; i < len; i++){ var cn = node.appendChild(this.createNode(cs[i])); if(this.preloadChildren){ this.doPreload(cn); } } node.endUpdate(); } return true; } return false; }, getParams: function(node){ var buf = [], bp = this.baseParams; if(this.directFn){ buf.push(node.id); if(bp){ if(this.paramOrder){ for(var i = 0, len = this.paramOrder.length; i < len; i++){ buf.push(bp[this.paramOrder[i]]); } }else if(this.paramsAsHash){ buf.push(bp); } } return buf; }else{ for(var key in bp){ if(!Ext.isFunction(bp[key])){ buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&"); } } buf.push("node=", encodeURIComponent(node.id)); return buf.join(""); } }, requestData : function(node, callback, scope){ if(this.fireEvent("beforeload", this, node, callback) !== false){ if(this.directFn){ var args = this.getParams(node); args.push(this.processDirectResponse.createDelegate(this, [{callback: callback, node: node, scope: scope}], true)); this.directFn.apply(window, args); }else{ this.transId = Ext.Ajax.request({ method:this.requestMethod, url: this.dataUrl||this.url, success: this.handleResponse, failure: this.handleFailure, scope: this, argument: {callback: callback, node: node, scope: scope}, params: this.getParams(node) }); } }else{ // if the load is cancelled, make sure we notify // the node that we are done this.runCallback(callback, scope || node, []); } }, processDirectResponse: function(result, response, args){ if(response.status){ this.handleResponse({ responseData: Ext.isArray(result) ? result : null, responseText: result, argument: args }); }else{ this.handleFailure({ argument: args }); } }, // private runCallback: function(cb, scope, args){ if(Ext.isFunction(cb)){ cb.apply(scope, args); } }, isLoading : function(){ return !!this.transId; }, abort : function(){ if(this.isLoading()){ Ext.Ajax.abort(this.transId); } }, /** *paramOrder: ['param1','param2','param3'] paramOrder: 'param1 param2 param3' paramOrder: 'param1,param2,param3' paramOrder: 'param1|param2|param'
Override this function for custom TreeNode node implementation, or to * modify the attributes at creation time.
* Example:* @param attr {Object} The attributes from which to create the new node. */ createNode : function(attr){ // apply baseAttrs, nice idea Corey! if(this.baseAttrs){ Ext.applyIf(attr, this.baseAttrs); } if(this.applyLoader !== false){ attr.loader = this; } if(typeof attr.uiProvider == 'string'){ attr.uiProvider = this.uiProviders[attr.uiProvider] || eval(attr.uiProvider); } if(attr.nodeType){ return new Ext.tree.TreePanel.nodeTypes[attr.nodeType](attr); }else{ return attr.leaf ? new Ext.tree.TreeNode(attr) : new Ext.tree.AsyncTreeNode(attr); } }, processResponse : function(response, node, callback, scope){ var json = response.responseText; try { var o = response.responseData || Ext.decode(json); node.beginUpdate(); for(var i = 0, len = o.length; i < len; i++){ var n = this.createNode(o[i]); if(n){ node.appendChild(n); } } node.endUpdate(); this.runCallback(callback, scope || node, [node]); }catch(e){ this.handleFailure(response); } }, handleResponse : function(response){ this.transId = false; var a = response.argument; this.processResponse(response, a.node, a.callback, a.scope); this.fireEvent("load", this, a.node, response); }, handleFailure : function(response){ this.transId = false; var a = response.argument; this.fireEvent("loadexception", this, a.node, response); this.runCallback(a.callback, a.scope || a.node, [a.node]); } });new Ext.tree.TreePanel({ ... new Ext.tree.TreeLoader({ url: 'dataUrl', createNode: function(attr) { // Allow consolidation consignments to have // consignments dropped into them. if (attr.isConsolidation) { attr.iconCls = 'x-consol', attr.allowDrop = true; } return Ext.tree.TreeLoader.prototype.call(this, attr); } }), ... });