View Full Version : Ext.ux.tree.TreePanel.NodeConverter

21 Aug 2009, 5:01 PM
Hi All

This is a some what special case plugin I have written for the treePanel.

In a project I am working on I need to load and allow the user to edit (via use of an 'inspector' panel) values of nodes on the tree and then extrapolate those values into the text that is *actually* displayed on the node in the tree. In other words I am making tools to manipulate none displayed data on the nodes of the tree which needs to be processed and displayed on the node after that data has been modified or loaded. I couldn't find any way of easily just modifying the node and then telling the tree to re-render it so built an interface for converting nodes after they have been rendered. I am not sure how many people this will be useful to but I figured I might as well post it just in case others were having the same issues as me.

The class includes some prebuilt functionality set up by the config of the class and also has full documentation with in the class.

If you find any bugs or have any comments please let me know.

Best Regards

Will Ferrer

author: Will Ferrer
date: 09/21/09
08/21/09 -- posted to ExtJS forums
09/02/09 -- fixed property documentation (property types were incorrectly documented)
09/14/09 -- looks for the nodeConverterFunction on both the parent and the plugin. Scopes calls to the nodeConverterFunction function to its self. Added allowExpandRoot.
04/19/10 -- made the converter handle branch nodes properly.
05/20/10 -- added loadConvertedNodes config option
06/10/10 -- convert function now returns the nodes that were converted

* @class Ext.ux.tree.TreePanel.NodeConverter
* @extends Ext.util.Observable
* A simple plug in to automatically run a function that modifies the attributes of nodes in a tree and then replaces the node in the tree with the newly modified node.
* By default conversion of a nodes will take place after the tree is first rendered, and any time a node is expanded (you may not want to use expanding animations).
* You can also trigger the conversion as at any time by calling [treePanel].nodeConverter.convert (see documentation of function for more details)
* @param {Object} config The config object
* @ptype ux-tree-treepanel-nodeconverter
* Notes:
* Important: Once this plugin has been applied to a tree you may access the plugin functions via [treePanel].nodeConverter -- IE: myTreePanel.nodeConverter.convert();
Ext.ux.tree.TreePanel.NodeConverter = function(config){
Ext.apply(this, config);
Ext.extend(Ext.ux.tree.TreePanel.NodeConverter, Ext.util.Observable, {
//Public Properties:
* @cfg {Boolean} activateOnAfterRender
* Whether or not to activate the converter function onAfterRender of the tree. Defaults to true.
activateOnAfterRender : true,
* @cfg {Boolean} onceOnlyAfterRender
* Auto trigger only after the initial render. Defaults to true.
onceOnlyAfterRender : true,
* @cfg {Boolean} onAfterRenderRecursive
* Whether or not the conversion should expand the child branches of the tree and activate it's self on the child nodes (recursively effecting the children in the tree). Defaults to false.
onAfterRenderRecursive : false,
* @cfg {Boolean} activateOnExpand
* Whether or not the conversion should happen when a node is expanded. Defaults to true.
activateOnExpand : true,
* @cfg {Object|Null} nodeConverterFunction
* The function to convert the nodes with -- need to accept one argument (the node being converted) and return that same node with what ever changes you want to make applied to it. Defaults to null.
nodeConverterFunction : null,
* @cfg {Boolean} rootNodeChildrenOnly
* Do not run the converter function on the root node of the tree -- if the root node is passed to the function have it automatically effect children of the root node only. Defaults to true.
rootNodeChildrenOnly : true,
* @cfg {Boolean} allowExpandRoot
* Whether or not the code should be allowed to try to expand the root node. Defaults to false.
allowExpandRoot : false,
* @cfg {Boolean} loadConvertedNodes
* Whether or not to automatically load nodes after they have been converted (important when converting a branch node, other wise the node may lose its child nodes). Defaults to true.
loadConvertedNodes : true,
//Private Properties:
* @private internal config {Boolean|Null} currentRecursive
* Whether the current function call should be recursive or not. Defaults to null.
currentRecursive : null,
* @private internal config {Object|Null} currentNodeConverterFunction
* currentFunction we are using for conversion. Defaults to null.
currentNodeConverterFunction : null,
* @cfg {Boolean} useColumnUIProvider
* whether or not to force the use column provider. Defaults to true.
useColumnUIProvider : true,
//Public Functions:
/** Public Function: convert
* starts the conversion function process
* @param {Array|Object|null} nodes (Optional) The node/nodes to start the conversion process on or null if you want to start with the root of the tree. Defaults to this.parent.getRootNode().
* @param {Boolean|null} recursive (Optional) Whether or not to recursively effect child nodes -- this will require expanding the child node if it is not expanded already. Defaults to false
* @param {Boolean|null} childrenOnly (Optional) Whether or not to only affect child nodes. Defaults to -- if node is root node: this.rootNodeChildrenOnly or false
* @param {Function|null} nodeConverterFunction (Optional) A function that will superseed the nodeConverterFunction set on the object
* @return {Boolean} true
convert : function (nodes, recursive, childrenOnly, nodeConverterFunction) {
var nodes = (typeof(nodes)!='undefined' && nodes!=null)?nodes:this.parent.getRootNode(),
nodes = (Ext.isArray(nodes))?nodes:[nodes],
recursive = (typeof(recursive)!='undefined' && recursive!=null)?recursive:false,
nodeConverterFunction = (typeof(nodeConverterFunction)!='undefined' && nodeConverterFunction!=null)?nodeConverterFunction:this.parent.nodeConverterFunction,
nodeConverterFunction = (typeof(nodeConverterFunction)!='undefined' && nodeConverterFunction!=null)?nodeConverterFunction:this.nodeConverterFunction,
n, curNode, returnNodes=[];

this.currentRecursive = recursive;
this.currentNodeConverterFunction = nodeConverterFunction;
for (n=0;n<nodes.length;n++) {
curNode = nodes[n];
returnNodes.push(this.convertNode(curNode, childrenOnly));

this.currentRecursive = null;
this.currentNodeConverterFunction = null;

return returnNodes;
//Private Functions:
// @private
init: function(parent){
this.parent = parent;
this.parent.nodeConverter = this;

if (this.activateOnAfterRender) {
var afterRenderOptions = (this.onceOnlyAfterRender)?{single:true}:{};
this.parent.on('afterrender', function() {
this.convert(null, this.onAfterRenderRecursive);
}, this, afterRenderOptions);
if (this.activateOnExpand) {
this.parent.on('expandnode', function(node) {
this.convert(node, false, true);
}, this);
// @private
convertNode : function (node, childrenOnly) {
var recursive = this.currentRecursive,
nodeConverterFunction = this.currentNodeConverterFunction,
newNode, convertedNode, isExpanded, artificialExpand=null, nodeRecompileAttributes, oldNodeRecompiled;
if (this.useColumnUIProvider) {
node.attributes.uiProvider = Ext.tree.ColumnNodeUI;
if (typeof(childrenOnly)=='undefined' || childrenOnly==null) {
var childrenOnly = (typeof(node.isRoot)!='undefined'&&node.isRoot)?this.rootNodeChildrenOnly:false;

if (!childrenOnly) {
if (node.parentNode.childNodes.length == 1) {
new Ext.tree.AsyncTreeNode({
nodeRecompileAttributes = node.toObject();
newNode = new Ext.tree.AsyncTreeNode(nodeRecompileAttributes);
//newNode.parentNode = node.parentNode;
//newNode.ownerTree = node.ownerTree;

newNode = nodeConverterFunction.call(this, newNode);
node.parentNode.replaceChild(newNode, node);
node = newNode;
if (this.loadConvertedNodes) {
var loader = node.loader || node.attributes.loader || node.getOwnerTree().getLoader();
loader.load(node, node.loadComplete.createDelegate(node, [false, false, null, null]), node);

if (!node.isLeaf() && (recursive || childrenOnly)) {
isExpanded = node.isExpanded();
if (!isExpanded && (!node.isRoot || this.allowExpandRoot)) {
artificialExpand = true;
node.eachChild(this.convert, this);
if (artificialExpand) {
return node;
Ext.preg('ux-tree-treepanel-nodeconverter', Ext.ux.tree.TreePanel.NodeConverter);

Sample plugin declaration (where you wanted to sum 2 none displayed values on the node):
ptype : 'ux-tree-treepanel-nodeconverter',
nodeConverterFunction : function (node) {
node.attributes.text = (node.attributes.name + node.attributes.value2);
return node;

Sample Usage: