PDA

View Full Version : [solved] Stateful TreePanel redux



billr
17 Feb 2011, 12:52 PM
I spent all of a long day figuring out how to do what everyone wants to do with a TreePanel: have it come up with the last-selected item selected, and with the nodes in the last collapsed/expanded state.

Three changes were needed: (1) have the loader initialize the nodes in the collapsed state (because I know how to expand a node (expandPath()) but I did not find a way to collapse a node); (2) set a CookieProvider; and (3) add a bunch of state code in the TreePanel configuration.

I've applied all these changes to xml-tree-loader.js from the XmlTreeLoader example. Here is the complete file; my changes are line 23 (true->false), and lines 46 and 68-109 (added).

First time, the tree comes up collapsed, with default text in the "Book Details" panel. Subsequently, it comes up the way you left it. I tested this on Mac/Firefox, and tested my own code on Win/IE.

I hope this helps someone, and I welcome suggestions for improvements. Thanks!



/*!
* Ext JS Library 3.3.1
* Copyright(c) 2006-2010 Sencha Inc.
* [email protected]
* http://www.sencha.com/license
*/

//
// Extend the XmlTreeLoader to set some custom TreeNode attributes specific to our application:
//
Ext.app.BookLoader = Ext.extend(Ext.ux.tree.XmlTreeLoader, {
processAttributes: function (attr) {
if (attr.first) { // is it an author node?
// Set the node text that will show in the tree since our raw data does not include a text attribute:
attr.text = attr.first + ' ' + attr.last;

// Author icon, using the gender flag to choose a specific icon:
attr.iconCls = 'author-' + attr.gender;

// Override these values for our folder nodes because we are loading all data at once. If we were
// loading each node asynchronously (the default) we would not want to do this:
attr.loaded = true;
attr.expanded = false;
} else if (attr.title) { // is it a book node?
// Set the node text that will show in the tree since our raw data does not include a text attribute:
attr.text = attr.title + ' (' + attr.published + ')';

// Book icon:
attr.iconCls = 'book';

// Tell the tree this is a leaf node. This could also be passed as an attribute in the original XML,
// but this example demonstrates that you can control this even when you cannot dictate the format of
// the incoming source XML:
attr.leaf = true;
}
}
});

Ext.onReady(function () {

var detailsText = '<i>Select a book to see more information...</i>';

var tpl = new Ext.Template('<h2 class="title">{title}</h2>', '<p><b>Published</b>: {published}</p>', '<p><b>Synopsis</b>: {innerText}</p>', '<p><a href="{url}" target="_blank">Purchase from Amazon</a></p>');
tpl.compile();

Ext.state.Manager.setProvider(new Ext.state.CookieProvider());

new Ext.Panel({
title: 'Reading List',
renderTo: 'tree',
layout: 'border',
width: 500,
height: 500,
items: [{
xtype: 'treepanel',
id: 'tree-panel',
region: 'center',
margins: '2 2 0 2',
autoScroll: true,
rootVisible: false,
root: new Ext.tree.AsyncTreeNode(),

// Our custom TreeLoader:
loader: new Ext.app.BookLoader({
dataUrl: 'xml-tree-data.xml'
}),

stateEvents: ['collapsenode', 'expandnode', 'click'],
stateId: 'tree-panel-state-id',
stateful: true,
getState: function () {
var nodes = [];
var lastnode;
this.getRootNode().eachChild(function (child) {
//function to store state of tree recursively
var storeTreeState = function (node, expandedNodes) {
if (node.isExpanded() && node.childNodes.length > 0) {
expandedNodes.push(node.getPath());
node.eachChild(function (child) {
storeTreeState(child, expandedNodes);
});
}
};
storeTreeState(child, nodes);
});
// In case this call was triggerd by click, save selected.
var selectedNode = this.getSelectionModel().getSelectedNode();
if (selectedNode) {
lastnode = selectedNode.getPath();
} else if (this.lastNode) {
lastnode = this.lastNode;
}
return {
lastNode: lastnode,
expandedNodes: nodes
}
},
applyState: function (state) {
var that = this;
this.getLoader().on('load', function () {
var nodes = state.expandedNodes;
for (var i = 0; i < nodes.length; i++) {
if (typeof nodes[i] != 'undefined') {
that.expandPath(nodes[i]);
}
}
that.selectPath(state.lastNode);
});
},

listeners: {
'render': function (tp) {
tp.getSelectionModel().on('selectionchange', function (tree, node) {
var el = Ext.getCmp('details-panel').body;
if (node && node.leaf) {
tpl.overwrite(el, node.attributes);
} else {
el.update(detailsText);
}
})
}
}
}, {
region: 'south',
title: 'Book Details',
id: 'details-panel',
autoScroll: true,
collapsible: true,
split: true,
margins: '0 2 2 2',
cmargins: '2 2 2 2',
height: 220,
html: detailsText
}]
});
});