PDA

View Full Version : [1.2] Ext.ux.tree.ArrayTree - Tree from array Extension (stateful, Ext 2.2 compat)



jsakalos
10 Apr 2008, 7:45 AM
Hi all!

Here is another of my extensions, very simlple one: http://arraytree.extjs.eu

Enjoy.

jsakalos
11 Apr 2008, 10:08 AM
Hi all,

version 1.1 (http://arraytree.extjs.eu) is out. It now keeps state (if state provider is available) of expanded/collapsed nodes.

Cheers,
Saki

galdaka
11 Apr 2008, 10:40 AM
Excellent jsakalos!!

Good work!!

Thanks,

spachner
22 Apr 2008, 8:04 AM
Hi Saki,

thanks for your arraytree extention, that was excactly I was looking for. But I experienced a glitch when collapsing the whole tree. Then the saved state is wrongly restored when restarting the app. When breaking within your hook 'afterRender' Firebug shows me that the object 'expandedNodes' contains two properties. Note that my root node's text is '|root'

1) undefined
2) |root "/|root"

Is seems that the first properties has the name "" with value undefined. The following code being executed then fails with this error

this.expandPath(this.expandedNodes[id]);


path has no properties
[Break on this error] var keys = path.split(this.pathSeparator);


Any idea?

regards

spachner

jsakalos
22 Apr 2008, 8:22 AM
Give ids to nodes. I've written that state restore routine for use with my examples at http://examples.extjs.eu and I haven't tested it extensively. I'll be only glad if you show me another use case or ideas... ;)

spachner
22 Apr 2008, 11:45 AM
Hi Saki,

my node have all ids. I checked your example and the difference to my implementation is that I do show the root's node which allows the user to collapse the whole tree to a single node. This is then the scenario I describes above where the bug appears.

Well, there is no hard need for showing the root's node, actually, so I do the same and set rootVisible:false. Then everything is working....

Thanks

spachner

jsakalos
22 Apr 2008, 12:06 PM
Looks like getPath returns nothing (null, undefined) then it is probably saved. First of all, clear all cookies (or state of another provider you may use) and try again. If it doesn't help try put an if around the error causing part and see how it behaves.

spachner
23 Apr 2008, 12:40 AM
Hi,

I fixed the problem inside afterRender() with the green code below



for(var id in this.expandedNodes) {
if(this.expandedNodes.hasOwnProperty(id)) {
// when root node is collapsed a wrong property 'expandedNodes[null]=undefined' is restored from cookie,
// get rid of them
if (this.expandedNodes[id] === undefined)
delete(this.expandedNodes[id]);
else {
this.expandPath(this.expandedNodes[id]);
}
}
}

jsakalos
23 Apr 2008, 3:05 AM
Well, if so, the test: if(!id) { continue; } seems better. Just ignore the invalid values. How does it behave then?

spachner
23 Apr 2008, 3:23 AM
Hi,

with the fix 'if(!id) { continue; }' it works as good as with my one.

regards

spachner

jsakalos
23 Apr 2008, 3:38 AM
I believe but there are less values saved as they are deleted. It may count in some situations...

AlainJS
19 Jun 2008, 12:35 AM
Hi saki,

I just wanted to mention a big problem with your ArrayTree extension.

Here is the use case :


var tree = new Ext.ux.tree.ArrayTree({
rootConfig:{
id:'root',
text:'Tree Root'
,visible:true
}
,renderTo:'treect'
,width:220
,height:300
,title:Ext.fly('page-title').dom.innerHTML
,children:[{
id: 't1',
text:'First Level Child 1'
,children:[{
id: 't1_1',
text:'Second Level Child 1'
}]
}]
});

console.log('root', tree.getNodeById('root'));
console.log('t1', tree.getNodeById('t1'));
console.log('t1_1', tree.getNodeById('t1_1'));


the last line returned 'undefined'.

you would have to do something like :


initComponent: function() {
this.on('beforeshow', {
fn: function(this_) {
//before 1st show, manually register the nodes so that getNodeById works
//this is a hack because the TreeLoader expects ajax
var registerNode_ = function(tree, n) {
tree.registerNode(n);
for (var i = 0; i < n.childNodes.length; i++) {
registerNode_(tree, n.childNodes[i]);
}
}
registerNode_(this_.tree, this_.tree.getRootNode());
},
single: true,
scope: this
});
}


see also http://extjs.com/forum/showthread.php?t=4595&page=2 and http://extjs.com/forum/showthread.php?t=30909

Regards,

jsakalos
19 Jun 2008, 10:49 AM
Hi saki,

I just wanted to mention a big problem with your ArrayTree extension.



What is the problem?

Sang
11 Aug 2008, 3:08 PM
saki,

Any plan to upgrade Ext.ux.tree.ArrayTree to Extjs 2.2?

jsakalos
12 Aug 2008, 12:13 AM
Yeah, I've done it internally long ago but I've forgotten to upload it to the server. Take a look now, it's there.

galdaka
14 Aug 2008, 6:31 AM
Hi,


What is the definitive code for restore state in a tree?
How can Instanciate it?


Thanks in advance,

jsakalos
14 Aug 2008, 8:26 AM
You can get last version together with example application at http://arraytree.extjs.eu

galdaka
14 Aug 2008, 8:47 AM
You can get last version together with example application at http://arraytree.extjs.eu

Hi jsakalos,

Thanks for example!!

Is posible create a plugin with your code as PLUGIN for all trees for maintain state?

or this plugin exits actually?


Thanks in advance,

jsakalos
14 Aug 2008, 8:55 AM
No, it is not plugin with the purpose of keeping state. It is extension with the added functionality of statefulness. You can take an inspiration from the code to write such plugin.

galdaka
19 Aug 2008, 7:46 AM
No, it is not plugin with the purpose of keeping state. It is extension with the added functionality of statefulness. You can take an inspiration from the code to write such plugin.

Hi Saki,

I used your code for write a restore state plugin for any tree. Here is code:



Ext.namespace("Ext.ux");
Ext.ux.StatefulTreePanelPlugin = function (config){
return {

tree : null,

init: function(tree){
this.tree = tree;
if(false !== tree.stateful) {
tree.expandedNodes = {};
tree.stateEvents = ['expandnode', 'collapsenode'];

tree.getState = function() {
return {expandedNodes:this.expandedNodes};
};

tree.on({
scope: this,
beforeclick: this.afterRender,
beforeexpandnode: this.beforeExpandNode,
beforecollapsenode: this.beforeCollapseNode
});
}
},
afterRender:function() {
if(confirm("Restore?")){

for(var id in this.tree.expandedNodes) {
if(this.tree.expandedNodes.hasOwnProperty(id)) {
this.tree.expandPath(this.tree.expandedNodes[id]);
}
}

}
},
beforeExpandNode:function(n) {
if(n.id) {
this.tree.expandedNodes[n.id] = n.getPath();
}
},
beforeCollapseNode:function(n) {
if(n.id) {
delete(this.tree.expandedNodes[n.id]);
n.cascade(function(child) {
if(child.id) {
delete(this.tree.expandedNodes[child.id]);
}
}, this);
}
}
}
}


For instanciate it:



tree = new Ext.tree.TreePanel({
el: this.container.getEl(),
animate : true,
...
plugins: [new Ext.ux.StatefulTreePanelPlugin()],
dropConfig : {appendOnly : true},
rootVisible : false
});


But I have a problem. I think that appropiate method for restore "expandedNodes" is afterRender but this event not exits, What event dou you think is appropiate for restore state? If I put "Render" fails because the node

jsakalos
19 Aug 2008, 1:30 PM
Nothing to say off the top of my head... It need some more research on order of events especially for AsyncTreeNodes...

RoDush
7 Oct 2008, 11:37 PM
Good time to all!
I am completly new to Ext... Could you give an advise how I can attach link to leaf of the ArrayTree? Because, as I understand, listener intersepts 'click' event when I click any node of the tree, has it childs or doesn't.

gym001
11 Nov 2008, 7:34 PM
In a accordion panel, have more than one arraytree, each tree have some children, but after render, each tree's rootnode have same chlidren?! for example : root-1's children = root-2's children [node-2.1 and node-2.2], why?

Thanks any reply ! :D

code is:


Ext.onReady(function(){
var panel_Tree = new Ext.Panel({
title:'NavBar',
width:170,
layout:'accordion',
layoutConfig:{animate:false},
defaults:{containerScroll:true, border:false},
items:[{
title:'panel 1',
items:[{
xtype:'arraytree',
border:false,
rootConfig:{text:'root-1', visible:false},
children:[{
text:'node-1.1',
leaf:true
},{
text:'node-1.2',
leaf:true
}]
}]
},{
title:'panel 2',
items:[{
xtype:'arraytree',
border:false,
rootConfig:{text:'root-2', visible:false},
children:[{
text:'node-2.1',
leaf:true
},{
text:'node-2.2',
leaf:true
}]
}]
}]
});

var viewport = new Ext.Viewport({
layout:'fit',
border:false,
items:[panel_Tree]
});
});

jsakalos
12 Nov 2008, 12:28 AM
Most likely it is same root for all trees.

gym001
12 Nov 2008, 2:18 AM
Is it a plugin's problem? or some error in Post#23's code? How to fix this error, I wish get your powerful help, Thanks!:">

jsakalos
12 Nov 2008, 8:32 AM
Finally I've found out that it was a bug in the ArrayTree code. The very beginning of initComponent should read:


,initComponent:function() {

// create root config
var cfg = Ext.apply(Ext.ux.clone(this.defaultRootConfig), this.rootConfig, {children:this.children});
You need to get Ext.ux.clone function for the fix to work - it has been hanging around on forums for some months...

gym001
12 Nov 2008, 7:21 PM
Great! Thanks for your help !!!

jasonb885
30 Jan 2009, 12:04 PM
I applied the same solution independently. Wish I looked here first. I had two trees and couldn't initially figure out why they were both identical.

I downloaded the stable version for Ext 2.2 yesterday. The fix seemingly isn't included in the tar.gz version?

jsakalos
31 Jan 2009, 2:11 AM
You can download clone function from here: http://extjs.com/forum/showthread.php?t=26644

jscharbe
11 Jun 2009, 5:08 PM
I need to do a reload on a array tree but don't see anyway, the tree.root.reload(fn) clears the existing data but reloads the old. Is there a way to do this? From what I see in the ext and ux code libraries it looks like I need to add my own extension.

Thanks, outside of reload it does exactly what I needed.

jasonb885
11 Jun 2009, 5:20 PM
I believe so. I used an override I found on the forums that provides a setRoot method:



// Allow replacement of existing rootNode
// http://extjs.com/forum/showthread.php?t=45885

Ext.override(Ext.tree.TreePanel, {
initComponent : function(){
Ext.tree.TreePanel.superclass.initComponent.call(this);

if(!this.eventModel){
this.eventModel = new Ext.tree.TreeEventModel(this);
}

// initialize the loader
var l = this.loader;
if(!l){
l = new Ext.tree.TreeLoader({
dataUrl: this.dataUrl
});
}else if(typeof l == 'object' && !l.load){
l = new Ext.tree.TreeLoader(l);
}
this.loader = l;

this.nodeHash = {};

/**
* The root node of this tree.
* @type Ext.tree.TreeNode
* @property root
*/
// setRootNode destroys the existing root, so remove it first.
if(this.root){
var r = this.root;
delete this.root;
this.setRootNode(r);
}

this.addEvents(

/**
* @event append
* Fires when a new child node is appended to a node in this 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 tree.
* @param {Tree} tree The owner tree
* @param {Node} parent The parent node
* @param {Node} node The child node removed
*/
"remove",
/**
* @event movenode
* Fires when a node is moved to a new location in the 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
*/
"movenode",
/**
* @event insert
* Fires when a new child node is inserted in a node in this 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 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 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 beforemovenode
* Fires before a node is moved to a new location in the 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
*/
"beforemovenode",
/**
* @event beforeinsert
* Fires before a new child is inserted in a node in this 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 beforeload
* Fires before a node is loaded, return false to cancel
* @param {Node} node The node being loaded
*/
"beforeload",
/**
* @event load
* Fires when a node is loaded
* @param {Node} node The node that was loaded
*/
"load",
/**
* @event textchange
* Fires when the text for a node is changed
* @param {Node} node The node
* @param {String} text The new text
* @param {String} oldText The old text
*/
"textchange",
/**
* @event beforeexpandnode
* Fires before a node is expanded, return false to cancel.
* @param {Node} node The node
* @param {Boolean} deep
* @param {Boolean} anim
*/
"beforeexpandnode",
/**
* @event beforecollapsenode
* Fires before a node is collapsed, return false to cancel.
* @param {Node} node The node
* @param {Boolean} deep
* @param {Boolean} anim
*/
"beforecollapsenode",
/**
* @event expandnode
* Fires when a node is expanded
* @param {Node} node The node
*/
"expandnode",
/**
* @event disabledchange
* Fires when the disabled status of a node changes
* @param {Node} node The node
* @param {Boolean} disabled
*/
"disabledchange",
/**
* @event collapsenode
* Fires when a node is collapsed
* @param {Node} node The node
*/
"collapsenode",
/**
* @event beforeclick
* Fires before click processing on a node. Return false to cancel the default action.
* @param {Node} node The node
* @param {Ext.EventObject} e The event object
*/
"beforeclick",
/**
* @event click
* Fires when a node is clicked
* @param {Node} node The node
* @param {Ext.EventObject} e The event object
*/
"click",
/**
* @event checkchange
* Fires when a node with a checkbox's checked property changes
* @param {Node} this This node
* @param {Boolean} checked
*/
"checkchange",
/**
* @event dblclick
* Fires when a node is double clicked
* @param {Node} node The node
* @param {Ext.EventObject} e The event object
*/
"dblclick",
/**
* @event contextmenu
* Fires when a node is right clicked. To display a context menu in response to this
* event, first create a Menu object (see {@link Ext.menu.Menu} for details), then add
* a handler for this event:<code><pre>
new Ext.tree.TreePanel({
title: 'My TreePanel',
root: new Ext.tree.AsyncTreeNode({
text: 'The Root',
children: [
{ text: 'Child node 1', leaf: true },
{ text: 'Child node 2', leaf: true }
]
}),
contextMenu: new Ext.menu.Menu({
items: [{
id: 'delete-node',
text: 'Delete Node'
}],
listeners: {
itemclick: function(item) {
switch (item.id) {
case 'delete-node':
var n = item.parentMenu.contextNode;
if (n.parentNode) {
n.remove();
}
break;
}
}
}
}),
listeners: {
contextmenu: function(node, e) {
// Register the context node with the menu so that a Menu Item's handler function can access
// it via its {@link Ext.menu.BaseItem#parentMenu parentMenu} property.
node.select();
var c = node.getOwnerTree().contextMenu;
c.contextNode = node;
c.showAt(e.getXY());
}
}
});
</pre></code>
* @param {Node} node The node
* @param {Ext.EventObject} e The event object
*/
"contextmenu",
/**
* @event beforechildrenrendered
* Fires right before the child nodes for a node are rendered
* @param {Node} node The node
*/
"beforechildrenrendered",
/**
* @event startdrag
* Fires when a node starts being dragged
* @param {Ext.tree.TreePanel} this
* @param {Ext.tree.TreeNode} node
* @param {event} e The raw browser event
*/
"startdrag",
/**
* @event enddrag
* Fires when a drag operation is complete
* @param {Ext.tree.TreePanel} this
* @param {Ext.tree.TreeNode} node
* @param {event} e The raw browser event
*/
"enddrag",
/**
* @event dragdrop
* Fires when a dragged node is dropped on a valid DD target
* @param {Ext.tree.TreePanel} this
* @param {Ext.tree.TreeNode} node
* @param {DD} dd The dd it was dropped on
* @param {event} e The raw browser event
*/
"dragdrop",
/**
* @event beforenodedrop
* Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
* passed to handlers has the following properties:<br />
* <ul style="padding:5px;padding-left:16px;">
* <li>tree - The TreePanel</li>
* <li>target - The node being targeted for the drop</li>
* <li>data - The drag data from the drag source</li>
* <li>point - The point of the drop - append, above or below</li>
* <li>source - The drag source</li>
* <li>rawEvent - Raw mouse event</li>
* <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
* to be inserted by setting them on this object.</li>
* <li>cancel - Set this to true to cancel the drop.</li>
* <li>dropStatus - If the default drop action is cancelled but the drop is valid, setting this to true
* will prevent the animated "repair" from appearing.</li>
* </ul>
* @param {Object} dropEvent
*/
"beforenodedrop",
/**
* @event nodedrop
* Fires after a DD object is dropped on a node in this tree. The dropEvent
* passed to handlers has the following properties:<br />
* <ul style="padding:5px;padding-left:16px;">
* <li>tree - The TreePanel</li>
* <li>target - The node being targeted for the drop</li>
* <li>data - The drag data from the drag source</li>
* <li>point - The point of the drop - append, above or below</li>
* <li>source - The drag source</li>
* <li>rawEvent - Raw mouse event</li>
* <li>dropNode - Dropped node(s).</li>
* </ul>
* @param {Object} dropEvent
*/
"nodedrop",
/**
* @event nodedragover
* Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
* passed to handlers has the following properties:<br />
* <ul style="padding:5px;padding-left:16px;">
* <li>tree - The TreePanel</li>
* <li>target - The node being targeted for the drop</li>
* <li>data - The drag data from the drag source</li>
* <li>point - The point of the drop - append, above or below</li>
* <li>source - The drag source</li>
* <li>rawEvent - Raw mouse event</li>
* <li>dropNode - Drop node(s) provided by the source.</li>
* <li>cancel - Set this to true to signal drop not allowed.</li>
* </ul>
* @param {Object} dragOverEvent
*/
"nodedragover"
);
if(this.singleExpand){
this.on("beforeexpandnode", this.restrictExpand, this);
}
},

setRootNode: function(node) {
// Already had one; destroy it.
if (this.root) {
this.root.destroy();
}

if(!node.render){ // attributes passed
node = this.loader.createNode(node);
}
this.root = node;
node.ownerTree = this;
node.isRoot = true;
this.registerNode(node);
if(!this.rootVisible){
var uiP = node.attributes.uiProvider;
node.ui = uiP ? new uiP(node) : new Ext.tree.RootTreeNodeUI(node);
}

// If we had previously rendered a tree, rerender it.
if (this.innerCt) {
this.innerCt.update('');
this.afterRender();
}
return node;
}
});


Usage:



var tree = new ...({...});
var cfg = {...};
tree.setRootNode(new Ext.tree.AsyncTreeNode(cfg));
// possibly not needed, depending on your usage pattern
tree.root.render();
tree.root.renderChildren();

Urkman
22 Aug 2010, 12:34 PM
Hello,

is this still the way to go for an ArrayTree in extJs 3.2.x?

Thanks and Greetings,
Urkman