If you have used ExtJS, you know that it contains a rich TreePanel control that, dare I say, rivals Flex in its functionality. The TreePanel control offers ColdFusion developers a very easy way to pre-populate the nodes of the Tree when the Tree loads, using nothing but Javascript. Take a look at the code below. (Note that this entire code block, and other Ext code, would normally be enclosed in an Ext.onReady(function(){}); command, but that code has been truncated for brevity.
The first line creates a new Ext.tree object. The next section creates a TreePanel object to display the contents of the tree on the page. Note that the TreePanel has an attribute called loader that is tied to a TreeLoader object. The TreeLoader allows us to specify a URL that will be called to load the array of nodes for the tree. The last section of the code renders the tree and expands thenodes to be visible once the tree renders.
var Tree = Ext.tree;
var tree = new Tree.TreePanel({
el:'tree-div',
useArrows:true,
autoScroll:true,
animate:true,
enableDD:true,
containerScroll: true,
rootVisible: false,
root: {
nodeType: 'async',
text: '--',
draggable:false,
id:'0'
},
loader: new Ext.tree.TreeLoader({
url:'/services/ObjectProxy.cfc?method=getChildren',
requestMethod:'GET',
preloadChildren: true,
baseParams:{}
})
});
// render the tree
tree.render();
tree.getRootNode().expand();
In this code, the TreeLoader calls url:'/services/ObjectProxy.cfc?method=getChildren'. Let's take a look at that function to see what data the tree needs to populate its nodes. This code is taken from our forthcoming open source application platform in its RoseCMS implementation.
<cffunction name="getChildren" access="remote" returntype="string" returnformat="plain">
<cfset var applicationFacade = application.RoseBeanFactory.getBean("applicationFacade")/>
<cfset var cmTreeManager = application.RoseBeanFactory.getBean("cmTreeManager")/>
<cfset var cmTreeNodeManager = application.RoseBeanFactory.getBean("cmTreeNodeManager")/>
<cfset var treeID = cmTreeManager.getcmTreesQuery(TreeName="Site Navigation").qList.TreeID />
<cfset var json = createobject("component","views.util.json")>
<!--- otherwise, get the tree from the database and set it into a TreeWalker --->
<cfset siteNavTree= application.RoseBeanFactory.getBean("cmTreeWalker") />
<cfset treeNodesQ = cmTreeNodeManager.getcmTreeNodesQuery(TreeID=treeID,orderby="ParentID, sortOrder asc") />
<cfset siteNavTree.setTree(treeNodesQ.qList) />
<cfset applicationFacade.set("siteNavTree",duplicate(siteNavTree))/>
<cfset tarray = json.encode(siteNavTree.getTree(),"array") />
<cfset tarray = replaceNoCase(tarray,"treenodeid","id","all")/>
<cfset tarray = replaceNoCase(tarray,"nodename","text","all")/>
<cfset tarray = replace(tarray,"PARENTID","parentid","all")/>
<cfset tarray = replace(tarray,"CHILDREN","children","all")/>
<cfset tarray = replace(tarray,"SORTORDER","sortorder","all")/>
<cfset tarray = replace(tarray,"OBJECTID","objectid","all")/>
<cfset tarray = replace(tarray,"TYPEID","typeid","all")/>
<cfset tarray = replace(tarray,"TREEID","treeid","all")/>
<cfset tarray = replace(tarray,"TYPENAME","typename","all")/>
<cfset tarray = replaceNoCase(tarray,"typename"":""webPage""","typename"":""webPage"",""icon"":""/scripts/ext/resources/images/default/tree/leaf.gif""","all")/>
<cfset tarray = replaceNoCase(tarray,"typename"":""menuSection""","typename"":""menuSection"",""icon"":""/scripts/ext/resources/images/default/tree/folder.gif""","all")/>
<cfreturn tarray />
</cffunction>
Again for brevity, I will skip the first section of the code and focus on the code that retrieves the tree in question and formats the nodes.
This line gets a cmTreeWalker object from ColdSpring:
<cfset siteNavTree= application.RoseBeanFactory.getBean("cmTreeWalker") />
cmTreeWalker is a mini-TreeWalker object designed to hold the contents of a tree. For now, let's just understand that it contains the definition of the Tree and its nodes and their relationships.
This line gets the nodes of the tree out of the database:
<cfset treeNodesQ = cmTreeNodeManager.getcmTreeNodesQuery(TreeID=treeID,orderby="ParentID, sortOrder asc") />
This line feeds the nodes into the cmTreeWalker object for organization into a tree:
<cfset siteNavTree.setTree(treeNodesQ.qList) />
This line drops the tree into the application scope, we're not concerned with it at this time:
<cfset applicationFacade.set("siteNavTree",duplicate(siteNavTree))/>
This line encodes the array of nodes into JSON format, which is what the TreeLoader object is expecting to receive:
<cfset tarray = json.encode(siteNavTree.getTree(),"array") />
The last section performs string manipulation on the encoded array. Without going into the reason for the string manipulation, I really like being able to perform string manipulation on JSON-encoded data like arrays.
But what do we actually get back? Take a look at the return from the browser. I have added line breaks for readability, but you get the picture.
[{"typeid":24,"id":1,"sortorder":1,"objectid":1,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"home","treeid":2,"parentid":"","children":
[{"typeid":30,"id":8,"sortorder":1,"objectid":13,"typename":"menuSection","icon":"/scripts/ext/resources/images/default/tree/folder.gif"
,"text":"Services","treeid":2,"parentid":1,"children":
[{"typeid":24,"id":22,"sortorder":0,"objectid":28,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Overview","treeid":2,"parentid":8,"children":
[]},{"typeid":24,"id":21,"sortorder":1,"objectid":27,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Staffing Solutions","treeid":2,"parentid":8,"children":
[]},{"typeid":24,"id":24,"sortorder":2,"objectid":30,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Project Based Consulting","treeid":2,"parentid":8,"children":
[]},{"typeid":24,"id":34,"sortorder":4,"objectid":41,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Strategic IT Consulting","treeid":2,"parentid":8,"children":
[]}]},{"typeid":30,"id":26,"sortorder":2,"objectid":32,"typename":"menuSection","icon":"/scripts/ext/resources/images/default/tree/folder.gif"
,"text":"Case Studies","treeid":2,"parentid":1,"children":
[{"typeid":24,"id":31,"sortorder":1,"objectid":37,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Business Credit Services","treeid":2,"parentid":26,"children":
[]},{"typeid":24,"id":32,"sortorder":2,"objectid":38,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Delaney Educational Enterprises","treeid":2,"parentid":26,"children":
[]},{"typeid":24,"id":33,"sortorder":3,"objectid":39,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Invitrogen","treeid":2,"parentid":26,"children":
[]}]},{"typeid":30,"id":11,"sortorder":3,"objectid":17,"typename":"menuSection","icon":"/scripts/ext/resources/images/default/tree/folder.gif"
,"text":"Company","treeid":2,"parentid":1,"children":
[{"typeid":24,"id":19,"sortorder":1,"objectid":25,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Who We Are","treeid":2,"parentid":11,"children":
[]},{"typeid":24,"id":17,"sortorder":2,"objectid":23,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Management","treeid":2,"parentid":11,"children":
[]},{"typeid":24,"id":12,"sortorder":3,"objectid":18,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Mission Statement","treeid":2,"parentid":11,"children":
[]},{"typeid":24,"id":18,"sortorder":4,"objectid":24,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Safe Harbor Policy","treeid":2,"parentid":11,"children":
[]}]},{"typeid":30,"id":27,"sortorder":5,"objectid":33,"typename":"menuSection","icon":"/scripts/ext/resources/images/default/tree/folder.gif"
,"text":"Community","treeid":2,"parentid":1,"children":
[{"typeid":24,"id":28,"sortorder":1,"objectid":34,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"San Diego Adobe Developers User Group ","treeid":2,"parentid":27,"children":
[]},{"typeid":24,"id":30,"sortorder":2,"objectid":36,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"March Mingle","treeid":2,"parentid":27,"children":
[]}]},{"typeid":24,"id":20,"sortorder":6,"objectid":26,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Blog","treeid":2,"parentid":1,"children":
[]},{"typeid":24,"id":6,"sortorder":8,"objectid":11,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Contact Us","treeid":2,"parentid":1,"children":
[]}]}]
The function creates a JSON-encoded representation of the tree nodes that Ext can digest.
Next time I will go into some detail about the format of the array of nodes in ColdFusion to give you an idea of how to format the data on the server before sending it off to Ext.