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

12.3k views Asked by At

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);
}
3

There are 3 answers

0
Ruan Mendes On BEST ANSWER

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.

1
Rob Boerman On

Until the treenode is expanded, the items are not yet initialized as ExtJS components. Until that time they are just sitting in the array of child items (xtyped standard javascript objects) look at TreeNode.childNodes in the API docs. Try removing them from that array and then expanding the item. That should do the trick

Cheers, Rob

3
user123444555621 On

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])