How to delete a node from an Ext TreePanel if the node hasn't been rendered

11,990

Solution 1

I finally figured it out, actually Condor from Ext-JS support figured it out for me.

The problem was that by default, a TreeLoader clears its children when TreeLoader#reload is called (it's called by AsyncTreeNode#expand(), if the clearOnLoad option is true (default).

 (TreeLoader) 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, [node]);
        }

So creating my tree loader with clearOnLoad: false fixes my problem. Except when I remove all the child nodes. To fix that I needed the following patch:

Ext.tree.TreeLoader.prototype.doPreload = 
Ext.tree.TreeLoader.prototype.doPreload.createSequence(
  function(node){
    if (node.attributes.children)  {
      node.attributes.children = [];    
    }
  }
);

Here's a link to the ext-js thread:

http://www.sencha.com/forum/showthread.php?122681-Deleting-unrendered-nodes-from-a-tree-doesn-t-work&p=567459#post567459

@Pumbaa: This is a solution, rather than a workaround as you suggested, but I'm really thankful for your help.

Solution 2

Ext.tree.TreePanel is the one component I hate most (followed by FormPanel).

Here is what happens: The TreeLoader preloads the root node's child nodes from its original config object, and so does a call to AsyncTreeNode.expand, which will basically reset the tree.

So, you're gonna have to remove the node from your rootNode's config like this before expanding:

tree.getRootNode().attributes.children[0].children.shift();

Edit: Actually, this is more intuitive:

tree.getRootNode().childNodes[0].attributes.children.shift();

(does the same thing, since root.childNodes[0].attributes === root.attributes.children[0])

Share:
11,990
Ruan Mendes
Author by

Ruan Mendes

Client side/ middle tier web developer. Have programmed in C, C++, C#, Groovy, Java, ActionScript, Lingo, JavaScript, PHP, TypeScript. Basic My language of choice is TypeScript, on the browser or Deno. Technologies I've worked with substantially: HTML, CSS, DOM, AJAX, Angular, React, jQuery, Google Closure Templates, Sencha touch, Ext-JS ASP, PHP, JSP, Struts, Velocity, Node.js, Kohana Windows, Unix, OpenVMS, Solaris Ant, make, maven XML-RPC, RESTful services JSUnit, JUnit, PhpUnit, Karma, Jasmine, js-test-driver, NUnit, YUI tests Selenium, Cucumber, Cypress Grails ASP.NET

Updated on June 04, 2022

Comments

  • Ruan Mendes
    Ruan Mendes almost 2 years

    I've found that I can't delete nodes that haven't been rendered yet. The following code shows what I mean. I ran it from the Chrome's (and Firebug's) command line while on http://dev.sencha.com/deploy/dev/exa...dow/hello.html (since that page has ext preloaded)

    I typed each statement separately to make sure there were no issues with asynchronous operations (even though the tree data is in memory)

    Ext.getBody.update('');
    // Tree with preloaded nodes in memory 
    var tree = new Ext.tree.TreePanel({ 
       renderTo: Ext.getBody(),  
       width: 300,  
       height: 500,  
       rootVisible: false, 
       loader: new Ext.tree.TreeLoader({preloadChildren:true}), 
       root: new Ext.tree.AsyncTreeNode({ 
         expandend: true, 
         children: [ 
            {text: 'Folder 1', id: 'folder1', leaf: false, children: [ 
                {text: 'File 1', id: 'file1', leaf: true}, 
                {text: 'File 2', id: 'file2', leaf: true} 
            ]} 
         ] 
       }) 
    }); 
    
    // Try to delete 'File 1', notice that the folder has never been expanded 
    tree.getRootNode().childNodes[0].childNodes[0].remove(true); 
    
    // Expand the node and see that 'File 1' is still there 
    tree.getRootNode().childNodes[0].expand(); 
    
    // Delete the first child 1 again, it works now that it's been rendered 
    tree.getRootNode().childNodes[0].childNodes[0].remove(true);
    

    Any suggestions on what to do?

    ANSWER

    var nodeToRemove = tree.getRootNode().childNodes[0].childNodes[0];
    if (!nodeToRemove.rendered) {
        var children = node.parentNode.attributes.children;
        Ext.each(children, function(child, index){
            if (child.id == nodeToRemove.id) {
                chilren.splice(index, 1);
                return false;
            }
        }) ;
    } else {
        nodeToRemove.remove(true);
    }
    
  • Ruan Mendes
    Ruan Mendes over 13 years
    Hi Rob: By using a treeloader with preloadChildren set to true, all the nodes in the children array are instantiated when the treepanel is rendered (though the corresponding TreeNodeUI is not rendered until it's expanded). What do you mean by remove them from that array? That is what my code is doing. You probably meant remove from the attributes.children array, but that doesn't work: attributes.children is irrelevant after the TreeNode objects have been created from the config and put into the childNodes array.
  • Ruan Mendes
    Ruan Mendes over 13 years
    I'm trying it now... you are saying that my code should work but the tree loader is not smart enough to detect that it's preloaded the children for the node and it recreates them from the attributes.children array?
  • Ruan Mendes
    Ruan Mendes over 13 years
    It works, thanks! I am going to look for the actual bug in their code.
  • Ansel Halliburton
    Ansel Halliburton over 13 years
    Right. Actually, it's not the loader's job to remember which nodes have been preloaded. The root node flags itself loaded. Still, Folder 1 is not flagged loaded, so once you call expand(), the loader jumps in again.