1. #1
    Sencha User
    Join Date
    Nov 2007
    Location
    London, UK
    Posts
    583
    Vote Rating
    0
    mabello is on a distinguished road

      0  

    Default TreeLoader extension to update a TreePanel

    TreeLoader extension to update a TreePanel


    I keep updating the extension in this first post

    Extension goal:
    The main goal of the extension is to extend the Ext.tree.TreeLoader so that you can update a TreePanel by manipulating normal "data object" that you can have loaded by an ajax call or in a different way. So you do not have to create an ad-hoc data to populate a TreePanel; it also help to understand better the public interface that a TreeLoader need to implement to be intagrated in the framework. (Attachment with code and examples at the bottom.)

    Development history:

    ************** (24 April 2008)
    * Version 0.2.3 *
    **************

    ===== Changed =====

    Ext.Tree.MyTreeLoader
    -Internal refactoring

    ===== Changed =====

    ****************


    **************
    * Version 0.2.2 *
    **************


    ===== Added =====

    Ext.tree.TreeNodeProvider class
    -Added new class Ext.tree.TreeNodeProvider as a better alternative to the prevoius treeNodeProvider object

    ===== Added =====

    ===== Fixed =====

    Ext.Tree.MyTreeLoader
    -Fixed load event, now it is correctly fired (if loadexception is not fired and beforeload does not return false of course, following the standard TreeLoader behaviour), thanks to tford for pointing out the bug

    ===== Fixed =====

    ****************


    **************
    * Version 0.2.1 *
    **************


    ===== Fixed =====
    Ext.Tree.MyTreeLoader
    -Fixed load function, now call node.destroy(), thanks to mykes for his/her kind help, more info here http://extjs.com/forum/showthread.php?t=14993
    ===== Fixed =====

    ****************



    **************
    * Version 0.2 *
    ************


    ===== Added =====

    Ext.Tree.MyTreeLoader
    -Added getTreeNodeProvider: function()-> get the treeNodeProvider
    -Added setTreeNodeProvider: function(newTreeNodeProvider)-> set a new treeNodeProvider

    ===== Added =====

    ****************


    ************
    * Version 0.1 *
    ************


    Ext.Tree.MyTreeLoader
    -Initial release

    ****************


    Tutorial end previous explanations

    Version 0.2.2 (examples are based on version 0.2.1)
    ----------------------------------------------------------------------------------
    FIX: load event is correctly fired (if loadexception is not fired and beforeload does not return false of course, following the standard TreeLoader behaviour), thanks to tford for pointing out the bug
    ADDED: new class Ext.tree.TreeNodeProvider as a better alternative to the prevoius treeNodeProvider object.
    You need to pass to the constructor the following configuration object:
    PHP Code:
    cfg = {
        
    dataobject //@optional, to preload data inside your TreeNodeProvider
        
    getNodes: function(dataToElaborate//@mandatory

    Example of use:
    PHP Code:
    var treeNodeProvider = new Ext.tree.TreeNodeProvider({
               
                
    getNodes: function(dataToElaborate) { //Here you process your data
                    
    var nodeArray = [];//The tree structure used to refresh the TreePanel
                    
    for(var 0dataToElaborate.lengthi++){
                        var 
    folder dataToElaborate[i];
                        var 
    filesInFolder folder.files;
                        
    //Create the parent node
                        
    var node = {
                            
    textfolder.name,
                            
    leaffalse,
                            
    children: []
                        }
                        
    //Create the children node
                        
    for(var 0filesInFolder.lengthj++) {
                            var 
    fileName filesInFolder[j];
                            var 
    childNode = {
                                
    textfileName,
                                
    leaftrue
                            
    }
                            
    //Set the children to the parent node
                            
    node.children.push(childNode);
                        }
                        
    //Add the parent node to the nodeArray
                        
    nodeArray.push(node);
                    }
            
    //throw 'exception';
                    //return the tree structure here
                    
    return nodeArray;
                }
             }); 
    ----------------------------------------------------------------------------------
    Version 0.2.1
    ----------------------------------------------------------------------------------
    FIX: load function now call node.destroy(), thanks to mykes for his/her kind help, more info here http://extjs.com/forum/showthread.php?t=14993

    ----------------------------------------------------------------------------------
    Version 0.2
    ----------------------------------------------------------------------------------
    Added getTreeNodeProvider: function()-> get the treeNodeProvider
    Added setTreeNodeProvider: function(newTreeNodeProvider)-> set a new treeNodeProvider
    ----------------------------------------------------------------------------------
    Version 0.1
    -------------
    -------------

    The main goal of the extension is to extend the Ext.tree.TreeLoader so that you can update a TreePanel by manipulating normal "data object" that you can have loaded by an ajax call or in a different way (for example if you want to create a menu with the TreePanel, you can "hardcode" the data in your javascript and display them in the TreePanel without any server side call)

    So for example, you can update a treeView manipulating an object like this one:
    PHP Code:
         var treeData1 = [
            {
                
    name"Folder1",
                
    files:['File1''File2''File3']
            },{
                
    name"Folder2",
                
    files:['Picture1''Picture2']
            }
         ]; 
    The core part you are interested in is actually the following part

    PHP Code:
    //Create your treeNodeProvider; it is an object that must implement these 2 methods (must "honour" this interface methods) :
         // setData(value)
         //getNodes(), returning the right structure that the treePanel can elaborate to refresh itself
         
    var treeNodeProvider = {
                
    data: [],//Property in which are set the data to elaborate
                
                
    getNodes: function() { //Here you process your data
                    
                    
    var nodeArray = [];//The tree structure used to refresh the TreePanel
                    
    for(var 0this.data.lengthi++){
                        var 
    folder this.data[i];
                        var 
    filesInFolder folder.files;
                        
    //Create the parent node
                        
    var node = {
                            
    textfolder.name,
                            
    leaffalse,
                            
    children: []
                        }
                        
    //Create the children node
                        
    for(var 0filesInFolder.lengthj++) {
                            var 
    fileName filesInFolder[j];
                            var 
    childNode = {
                                
    textfileName,
                                
    leaftrue
                            
    }
                            
    //Set the children to the parent node
                            
    node.children.push(childNode);
                        }
                        
    //Add the parent node to the nodeArray
                        
    nodeArray.push(node);
                    }
                    
    //return the tree structure here
                    
    return nodeArray;
                },
                
    setData: function(data) {//Called internally by Ext.tree.MyTreeLoader by the method updateTreeNodeProvider
                    
    this.data data;
                },
                
    scopethis//Could be useful to use when you elaborates data to switch the context...not used in this example and it's not required
             
    }; 
    Every time you click to the refresh button in the example, I simulate to load different data, in our case the data we want to elaborate is a structure like the following:

    PHP Code:
    //Data to load in the tree
         
    var treeData1 = [
            {
                
    name"Folder1",
                
    files:['File1''File2''File3']
            },{
                
    name"Folder2",
                
    files:['Picture1''Picture2']
            }
         ];
          
    //Data to load in the tree
         
    var treeData2 = [
            {
                
    name"Pictures",
                
    files:['Doc1.txt''Doc2.txt']
            },{
                
    name"Documents",
                
    files:['xxx.txt''yyy.txt']
            }
         ]; 
    In this case, at the beginning the tree is empty, but you can preload the data in the treeLoader if you remove the comment of this line in the example.

    PHP Code:
     myTreeLoader.updateTreeNodeProvider(treeData1); 
    The rendering part is the same of a normal TreePanel, but the way you load the data is different and the work to refresh the tree is this:

    PHP Code:
    var rootNode treePanel.getRootNode();//get the root node
    var loader treePanel.getLoader();//Get the loader, note that is of type MyTreeLoader
    loader.updateTreeNodeProvider(treeData2);//update the treeNodeProvider associated to the    loder, but you DON't reload the TreePanel yet!!!
    loader.load(rootNode);//Actually refresh the TreePanel UI 
    In this way you can load the data in the way you prefer and elaborate the data in getNodes function of the treeNodeProvider;
    the data can be in the format you like but in getNodes method you need to return a structure that the treePanel can udertend to render itself.

    Please feel free to contact me if something is not clear and give me suggestions and feedback so I can improve the code and the example.

    I have found it very usefull in my application because you can load the data in the way you want (ajax call or in other way) and then elaborate the data in getNodes.

    NEW EXAMPLE 2:

    Now, I have updated the extension with these two methods:
    PHP Code:
    getTreeNodeProvider: function(){
            return 
    this.treeNodeProvider;
        },
        
    setTreeNodeProvider: function(newTreeNodeProvider) {
            if(
    newTreeNodeProvider == null || (typeof newTreeNodeProvider =='undefined'))
                throw 
    'setTreeNodeProvider, newTreeNodeProvider == null || (typeof newTreeNodeProvider == undefined)';
            
    this.treeNodeProvider newTreeNodeProvider;
        } 
    Now, I have created an example in which I'm simulating some ajax calls and I'm actually using two different treeNodeProvider to reload my treePanel in different way (I really like it).

    So here we are, first treeNodeProvider:
    PHP Code:
    var treeNodeProvider = {
                
    data: [],//Property in which are set the data to elaborate
                
                
    getNodes: function() { //Here you process your data
                    
                    
    var nodeArray = [];//The tree structure used to refresh the TreePanel
                    
    for(var 0this.data.lengthi++){
                        var 
    folder this.data[i];
                        var 
    filesInFolder folder.files;
                        
    //Create the parent node
                        
    var node = {
                            
    textfolder.name,
                            
    leaffalse,
                            
    children: []
                        }
                        
    //Create the children node
                        
    for(var 0filesInFolder.lengthj++) {
                            var 
    fileName filesInFolder[j];
                            var 
    childNode = {
                                
    textfileName,
                                
    leaftrue
                            
    }
                            
    //Set the children to the parent node
                            
    node.children.push(childNode);
                        }
                        
    //Add the parent node to the nodeArray
                        
    nodeArray.push(node);
                    }
                    
    //return the tree structure here
                    
    return nodeArray;
                },
                
    setData: function(data) {//Called internally by Ext.tree.MyTreeLoader by the method updateTreeNodeProvider
                    
    this.data data;
                },
                
    scopethis//Could be useful to use when you elaborates data to switch the context...not used in this example and it's not required
             
    }; 
    The second treeNodeProvider
    PHP Code:
     var treeNodeProvider2 = {
                
    data: [],//Property in which are set the data to elaborate
                
                
    getNodes: function() { //Here you process your data
                    
    return  Ext.decode(this.data); 
                },
                
    setData: function(data) {//Called internally by Ext.tree.MyTreeLoader by the method updateTreeNodeProvider
                    
    this.data data;
                },
                
    scopethis//Could be useful to use when you elaborates data to switch the context...not used in this example and it's not required
             
    }; 
    Simulating a first AjaxCall in which I'm using the first treeNodeProvider to refresh the tree with nodes:
    PHP Code:
     var ajaxCallGetDataForTree = function(inputParameters){
                
    treePanel.body.mask("Loading data...");
                
    setTimeout(ajaxCallbackGetDataForTree5000);
             }
             
             var 
    ajaxCallbackGetDataForTree = function(){
                
    treePanel.body.unmask();
                
    treePanel.body.highlight('#c3daf9', {block:true});
                
    //Simulating that I have received the response that is treeData2 from the callback of the ajaxCall
                 
    var rootNode treePanel.getRootNode();//get the rootnode
                 
    var loader treePanel.getLoader();//Get the loader, note that is of type MyTreeLoader
                 
    loader.updateTreeNodeProvider(treeData2);
                 
    loader.load(rootNode);
             } 
    The second Ajax call, you probably are interested on this one, because I'm simulating to receive a Json string from the server (in the callback of the ajax call):
    The response form the server is the following indeed:
    PHP Code:

        
    var serverResponseString '[{"text":"Ray Abad","id":"contacts\/11","cls":"file","leaf":"true","listeners": { "click": function(node, eventObject){ alert(node);}} },{"text":"Kibbles Bits","id":"contacts\/9","cls":"file","leaf":"true","listeners":"userNodeInterceptor"},{"text":"Johnny Bravo","id":"contacts\/18","cls":"file","leaf":"true","listeners":"userNodeInterceptor"},{"text":"Mike Bridge","id":"contacts\/13","cls":"file","leaf":"true","listeners":"userNodeInterceptor"},{"text":"Jane Brown","id":"contacts\/2","cls":"file","leaf":"true","listeners":"userNodeInterceptor"},{"text":"Jim Brown","id":"contacts\/19","cls":"file","leaf":"true","listeners":"userNodeInterceptor"}]'
    The ajax call is:
    PHP Code:
     var ajaxCallGetDataForTree2 = function(inputParameters){
                
    treePanel.body.mask("Loading data...");
                
    setTimeout(ajaxCallbackGetDataForTree25000);
             }
             
             var 
    ajaxCallbackGetDataForTree2 = function(){
                
    treePanel.body.unmask();
                
    treePanel.body.highlight('#c3daf9', {block:true});
                
    //Simulating that I have received the response that is treeData2 from the callback of the ajaxCall
                 
    var rootNode treePanel.getRootNode();//get the rootnode
                 
    var loader treePanel.getLoader();//Get the loader, note that is of type MyTreeLoader
                 
    loader.updateTreeNodeProvider(serverResponseString);
                 
    loader.load(rootNode);
             } 
    Now, please take a look at the ttbar items, I'm using the following code:
    PHP Code:
     tbar:[{
                    
    text"Click here to Refresh using treeNodeProvider",
                    
    handler: function(){
                        
    //Simulating change the treeNodeProvider of the loader
                        
    var rootNode treePanel.getRootNode();//get the rootnode
                        
    var loader treePanel.getLoader();//Get the loader, note that is of type MyTreeLoader
                        
    loader.setTreeNodeProvider(treeNodeProvider);
                        
    //Simulating an ajax call
                        
    ajaxCallGetDataForTree();
                    }    
                },{
                    
    text"Click here to Refresh using treeNodeProvider2",
                    
    handler: function(){
                        
    //Simulating change the treeNodeProvider2 of the loader
                        
    var rootNode treePanel.getRootNode();//get the rootnode
                        
    var loader treePanel.getLoader();//Get the loader, note that is of type MyTreeLoader
                        
    loader.setTreeNodeProvider(treeNodeProvider2);
                        
    //Simulating a different ajax call
                        
    ajaxCallGetDataForTree2();
                    }    
                }] 
    In the handler, before I simulate the ajax call, I set a different treeNodeProvider in the loader:
    PHP Code:
    loader.setTreeNodeProvider(treeNodeProvider2); 
    Of course, you can do that in the ajaxcall or in the ajaxCallback that could be better I think, but mine is only an example

    If you want to preolad your tree, you need to use this line of code before creating the treeLoader (as you can see in the example):
    PHP Code:
    myTreeLoader.updateTreeNodeProvider(treeData1);//if you want to "preload" the TreePanel with this data 
    Thanks and keep up the good work with Ext!!!
    Attached Files

  2. #2
    Ext User
    Join Date
    Feb 2008
    Location
    Spain
    Posts
    173
    Vote Rating
    0
    Capi666 is on a distinguished road

      0  

    Default


    Hw can render it?

    I

  3. #3
    Ext User
    Join Date
    Feb 2008
    Location
    Spain
    Posts
    173
    Vote Rating
    0
    Capi666 is on a distinguished road

      0  

    Default


    ok, perfect!!!

    My code html:

    Code:
    ...
    ...
    <div id="container">
        <div id="toolbar"></div>
    	<div id="divtree"></div>
    </div>
    ...
    ...

    And the js:

    Code:
    Ext.tree.MyTreeLoader = function(config){
    
        Ext.apply(this, config);
        Ext.tree.MyTreeLoader.superclass.constructor.call(this, config);
    
    };
    
    Ext.tree.MyTreeLoader = Ext.extend(Ext.tree.MyTreeLoader, Ext.tree.TreeLoader, {
        
        treeNodeProvider: null,
        
        loading: false,
    
        load : function(node, callback){
            if(this.clearOnLoad){
                while(node.firstChild){
                    node.removeChild(node.firstChild);
                }
            }
            if(this.doPreload(node)){ // preloaded json children
                if(typeof callback == "function"){
                    callback();
                }
            }else if(this.treeNodeProvider){
                this.requestData(node, callback);
            }
        },
    
        requestData : function(node, callback){
            if(this.fireEvent("beforeload", this, node, callback) !== false){
                    this.loading = true;
                    var nodesToAdd = this.treeNodeProvider.getNodes();
                    if(nodesToAdd)
                        this.processResponse(nodesToAdd, node, callback);
                     this.loading = false;
            } else {
                // if the load is cancelled, make sure we notify
                // the node that we are done
                if(typeof callback == "function"){
                    callback();
                }
            }
        },
    
        isLoading : function(){
            return this.loading;
        },
    
        abort : function(){
        },
    
        processResponse : function(o, node, callback){
            try {
                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();
                if(typeof callback == "function"){
                    callback(this, node);
                }
            }catch(e){
                this.fireEvent("loadexception", this, node, e);
                if(typeof callback == "function"){
                    callback(this, node);
                }
            }
        },
        
        updateTreeNodeProvider: function(obj){
            if(this.treeNodeProvider)
                this.treeNodeProvider.setData(obj);
        }
    });
    
    Ext.onReady(function(){
         
         var treeNodeProvider = {
                data: {},
                
                getNodes: function() {//Usually you will use this.data, which contains the dataObject to create your tree structure
                    var nodeArray = [];
    				var NombresArray = ['Nombre Jefe','1','2','3','4','5','6'];
                   
    				var node = {
    					text: NombresArray[0],
    					leaf: false,
    					children: []
    				}
    				for (i=1;i<NombresArray.length; i++){
    					
    					var childNode = {
    						text: NombresArray[i],
    						leaf: true
    					}
    			
    					node.children.push(childNode);
    				}
    				
    				nodeArray.push(node);
    
                    return nodeArray;
                },
                setData: function(data) {
                    this.data = data;
                },
                scope: this
             };
             
             var myTreeLoader = new Ext.tree.MyTreeLoader({
                treeNodeProvider: treeNodeProvider
             });
             
             myTreeLoader.updateTreeNodeProvider({});//Usually you need to pass a dataObject
             
             var basePanelCfg = {
                preloadChildren: true,
                lines: false,
                clearOnLoad: true,   	  
                rootVisible: false,
                containerScroll: true,
                frame: false,
                collapsible: false,
                animate: true,
                loader: myTreeLoader,
             };
    
             //var treePanel = new Ext.tree.TreePanel(basePanelCfg);
    		 var treePanel = new Ext.tree.TreePanel(
        	{
                preloadChildren: true,
                lines: false,
                clearOnLoad: true,   	  
                rootVisible: false,
                containerScroll: true,
                frame: false,
                collapsible: false,
                animate: true,
                loader: myTreeLoader,
        	});
             
            var root = new Ext.tree.AsyncTreeNode({
                text: 'Root',
                draggable: false
            });
    		
            treePanel.setRootNode(root);
            treePanel.render('divtree');
    		
    });
    Lot of thanks,

    PD: I add reputation for you

  4. #4
    Sencha User
    Join Date
    Nov 2007
    Location
    London, UK
    Posts
    583
    Vote Rating
    0
    mabello is on a distinguished road

      0  

    Default Thanks

    Thanks


    Hey thank you very much, so kind

    And your example is better than mine, so thanks

    Only one thing, if you want to run your example in IE6 you need to remove 2 extra comma you have in your code:

    HTML Code:
    var basePanelCfg = {
                preloadChildren: true,
                lines: false,
                clearOnLoad: true,   	  
                rootVisible: false,
                containerScroll: true,
                frame: false,
                collapsible: false,
                animate: true,
                loader: myTreeLoader,<-----------------
             };
    
             //var treePanel = new Ext.tree.TreePanel(basePanelCfg);
    		 var treePanel = new Ext.tree.TreePanel(
        	{
                preloadChildren: true,
                lines: false,
                clearOnLoad: true,   	  
                rootVisible: false,
                containerScroll: true,
                frame: false,
                collapsible: false,
                animate: true,
                loader: myTreeLoader,<----------------
        	});
    Of course FF is Ok, but IE6 will complain...

    Thanks again

  5. #5
    Ext User
    Join Date
    Feb 2008
    Location
    Spain
    Posts
    173
    Vote Rating
    0
    Capi666 is on a distinguished road

      0  

    Default


    Yes, the last commas always deleted it... But, now forget it...

    My sample better? I don

  6. #6
    Ext User
    Join Date
    Feb 2008
    Location
    Spain
    Posts
    173
    Vote Rating
    0
    Capi666 is on a distinguished road

      0  

    Default


    How can expand automatically the tree the first time when acced to the page?

    Thanks,

  7. #7
    Sencha User
    Join Date
    Nov 2007
    Location
    London, UK
    Posts
    583
    Vote Rating
    0
    mabello is on a distinguished road

      0  

    Default treePanel.expandAll();

    treePanel.expandAll();


    You can use:
    HTML Code:
    treePanel.expandAll();
    Of course the tree must be already rendered.

  8. #8
    Ext User
    Join Date
    Feb 2008
    Location
    Spain
    Posts
    173
    Vote Rating
    0
    Capi666 is on a distinguished road

      0  

    Default


    Yes, lot of thanks!!

  9. #9
    Ext User
    Join Date
    Feb 2008
    Location
    Spain
    Posts
    173
    Vote Rating
    0
    Capi666 is on a distinguished road

      0  

    Default


    How can know the position that i select?

    Thanks,

  10. #10
    Ext User
    Join Date
    Feb 2008
    Location
    Spain
    Posts
    173
    Vote Rating
    0
    Capi666 is on a distinguished road

      0  

    Default


    Sorry, it