Listening for CFTREE data loaded events

Last week Ray Camden asked if it was possible to listen for data loaded events with CFTREE. I had done some work with CFTREE in the past and I knew something like this should be possible. After some quick investigation I put together a quick and dirty example to show how you could be notified when new nodes are loaded for a tree.

<html>
<head>
<title>Example: ColdFusion.Tree.loadNodes</title>
<script language="JavaScript">
ColdFusion.Tree.loadNodes = (function (old) {
return function (nodesArray, params) {
if (typeof old == 'function') old.apply(this,arguments);
myCallBack();
};
})(ColdFusion.Tree.loadNodes);

function myCallBack() {
//console.log("hello"); alert("hello");
}
</script>
</head>
<body>
<h1>Example: Calling a method after nodes load.</h1>
<cfform name="testform">
<cftree name="t1" format="html">
<cftreeitem bind="cfc:makeTree.getNodes({cftreeitemvalue},{cftreeitempath})">
</cftree>
</cfform>
</body>
</html>

Ray pointed out that this would only work if you had one tree on a page because all trees would fire the same callback function. With a little bit more work I put together the following, which allows you to add listeners to all trees or target listeners to specific trees.

<html>
<head>
<title>Example: ColdFusion.Tree.loadNodes</title>
<script language="JavaScript">   
      // add an object to hold our listeners       ColdFusion.Tree.loadNodesListeners = {};
      // add a convenience method to add a listener       ColdFusion.Tree.addLoadNodesListener = function(listener,treeid) {
         var key = treeid ? treeid : "_all_";
         if (!this.loadNodesListeners[key])
            this.loadNodesListeners[key] = [];
         this.loadNodesListeners[key].push(listener);
      };
    // override loadNodes to call our listeners; ColdFusion.Tree.loadNodes = (function (old) {
return function (nodesArray, params) {
            if (typeof old == 'function') old.apply(this,arguments);
            //call listeners not assigned to a specific treeid             if (ColdFusion.Tree.loadNodesListeners._all_) {
               for (var i = 0; i < ColdFusion.Tree.loadNodesListeners._all_.length; ++i) {
                  var listener = ColdFusion.Tree.loadNodesListeners._all_[i];
                  listener.apply(this, arguments);
               }
            }
            //get treeid and call treeid specific listeners             var treeid = params.treeid;
            if (ColdFusion.Tree.loadNodesListeners[treeid]) {
               for (var i = 0; i < ColdFusion.Tree.loadNodesListeners[treeid].length; ++i) {
                  var listener = ColdFusion.Tree.loadNodesListeners[treeid][i];
                  listener.apply(this,arguments);
               }
            }            
};
})(ColdFusion.Tree.loadNodes);
      
      
      function treeListener(){
         //console.log("tree listener");          alert("tree listener");
      }
      
      function treeOneListener() {
         //console.log("tree one listener");          alert("tree one listener");
      }
      
      function treeTwoListener() {
         //console.log("tree two listener");          alert("tree two listener");
      }
      
      function init() {
         ColdFusion.Tree.addLoadNodesListener(treeListener);               
         ColdFusion.Tree.addLoadNodesListener(treeOneListener,"t1");         
         ColdFusion.Tree.addLoadNodesListener(treeTwoListener,"t2");         
      }
      
      ColdFusion.Event.registerOnLoad(init);

      
</script>
</head>
<body>
<h1>Example: Calling a method after nodes load.</h1>
<h2>Tree 1</h2>
<cfform name="testform">
<cftree name="t1" format="html">
<cftreeitem bind="cfc:makeTree.getNodes({cftreeitemvalue},{cftreeitempath})">
</cftree>
</cfform>
<h2>Tree 2</h2>
<cfform name="testform">
<cftree name="t2" format="html">
<cftreeitem bind="cfc:makeTree.getNodes({cftreeitemvalue},{cftreeitempath})">
</cftree>
</cfform>
</body>
</html>

Both of these examples call the following CFC to load dummy data. The CFC has a Sleep() call to delay the response so you can see the callbacks only fire after we get results back from the server.

<cfcomponent>
<cffunction name="getNodes" returnType="array" output="no" access="remote">
<cfargument name="nodeitemid" required="true" />
<cfargument name="nodeitempath" required="true" />

<cfset var nodeArray = ArrayNew(1) />
<cfset var element = StructNew() />
<cfset var i = "" />

<!--- the initial value of the top level is the empty string --->
<cfif nodeitemid IS "">
<cfset nodeitemid =0>
</cfif>

    <!--- sleep so we can see the server working --->
<cfset Sleep(3000) />

<!--- create a array with elements defining the child nodes --->
<cfloop from="1" to="#RandRange(1,4)#" index="i">
<cfset StructClear(element) />
<cfset element.value = "#nodeitemid#.#i#" />
<cfset element.display = "Node #element.value#" />
<cfset element.expand = "false" />
<cfset element.href = "index.cfm" />
<cfset element.leafnode = "false" />
<cfset element.target = "_blank" />
<cfset nodeArray[i] = Duplicate(element) />
</cfloop>
<cfreturn nodeArray />
</cffunction>
</cfcomponent>

Hopefully this will help if you need to do something with a CFTREE after data is loaded from the server.

Comments
BlogCFC was created by Raymond Camden. This blog is running version 5.8.001.