CFTree populated with Flash remoting and XML
As part of these series “How to populate a cf[…] with Remoting”, I wanted to show how to bring data into a cftree. This is the easiest way, but it has some limitations: your xml data has to have a label attribute and the tree is first closed when it loads. If you want to get around those, you will have to do more work with the data returned from the server (I may do it if I have time :)).
The function that handles the response it’s the same as in the cfgrid example:
responseHandler.onResult = function( results: Object ):Void {
//when results are back, populate the cftree
categories.dataProvider = results;
}
The main difference is in the cfc function. In this case, it will return an xml object. Each node has a label attribute and any other attribute we want. Important: You must remove all whitespace from the xml string.
<cffunction name="getCategories" access="remote" output="false" hint="Gets a tree in xml form">
<cfset var categories = ""/>
<!--- possibly make a query to get categories--->
<!--- make the xml --->
<!--- it looks awful, but all whitespace must be removed --->
<cfxml variable="categories"><categories label="Categories" id="root"><category id="coldfusion" label="ColdFusion">the rest of the nodes...</category></categories>
</cfxml>
<cfreturn categories />
</cffunction>
And the form:
<cfform name="myform" format="Flash">
<cftree name="categories"><!--- an empty tree --->
</cftree>
<cfinput type="text" label="Selected Item:" name="display" bind="{categories.selectedNode.getProperty('id')} = {categories.selectedNode.getProperty('label')}">
<cfinput type="button" name="getValues" value="Populate tree" onClick="#getData#">
</cfform>
Note that to access other attributes in the xml node, you must reference them as:
myTree.selectedNode.getProperty('myAtribute');
CarlosM()
I just want to say that the last few post from this blog have really inspired me and updating my code to use these features have taught me a lot, Thanks.
If you could help me on this I would appreciate it.:)
I’ve created a complex tree structure with several queries and a cftree format=”object” in a CFC.
When I pass the variable from the CFC to populate the updateable cftree in the CFM, I get the structure but no content in the tree. The tree shows “ [object Object],1 ,,, null “ instead of the data.
Is there a way to fix this? Or do I have convert the object in the CFC to an xml string before I pass it and if so how??? *<;) Did that all make sense?
Thanx again
Brian Rinaldi
<cftree name="searchTree">
<cfloop query="qryTree">
<cftreeitem value="#qryTree.id#" display="#qryTree.title#" parent="#qryTree.parent#" expand="false" />
</cfloop>
</cftree>
I am going crazy trying to figure this out...any help you can offer would be greatly appreciated.
Brian Rinaldi
categories.selectedNode.getProperty('data').value
CarlosM()
"This is the easiest way, but it has some limitations: your xml data has to have a label attribute and the tree is first closed when it loads. If you want to get around those, you will have to do more work with the data returned from the server (I may do it if I have time :))."
Do you think you can post on how to work around these limitation? The label attribute is keeping me from remoting data in from a search.
Stephen Moretti
My code (pretty much your code, except with a query building the XML) works great from a button, but when I call it from onLoad I see the tree populate and then clear out.
Any insights would be greatfully received.
Laura
the problem is that after your data has loaded, the regular data tree gets loaded and it overwrites yours (since the original tree was empty, you see an empty tree).
I don't know what you need to do, but the simplest way to avoid that is by having only the code that sets up the icons in the onload, and get the tree items normally. In this way, the tree will load, and it will acquire the new icons you set. That is, skip the remoting call. The only thing you will not be able to do is to set up an icon to a specific node, since the tree is not yet populated when you call it from the onload function.
Laura
The first paragraph is still true. But if you just want to load a tree, I would use it as normal. No need to make a remoting call onload really, since that is exactly what the cfform does by itself unless you need to do some manipulation, like adding icons.
Stephen Moretti
You're right. I should be loading the data normally in the first instance. I think I was having a Sunday afternoon brain failure moment :)
Stephen Moretti
If you load a cftree using a query and cfformtreeitem, data in the tree is accessed using categories.selectedNode.getProperty('data').value
If you load data into a cftree via an XML packet then you access the data using
categories.selectedNode.getProperty('data') without .value on the end.
I don't know whether this is a bug or purposeful, but its very annoying and means I have to check to see if I'm actually getting a value back from the tree if I load it up from two different sources.
Hence the reason why I was trying to standardise the way I populate the tree to grabbing data from a remote connection and XML packet.
Carl Sawyer
Nahuel
Are you using remoting for that?
If that is the case, once you set the data to the tree, you need to loop for the node that you want .
Then, open all the parents of that specific node until you reach the root. Lastly, selected it. It is a lot of work, so we may make a small example of that.
Carl Sawyer
Thanks for the reply. No we are not using remoting for this because it wasn't necessary (but maybe now it is). What I can't understand is why the following won't work:
myTree.selectedNode = myTree.getNodeAt(i);
where it would be index corresponding to the node we want selected. We can't seem to find what methods and properties (in actionscript) are available to us on the cftree.
Laura
You could do that from a click of a button for example (I think the node has to be opened to do it). But you can't do it right after onload. First, because there is no onload :) Second, even if you use the onload hack, the biggest problem is that when the form loads, the data has not yet arrived, so running the above code will do nothing, as the tree would be empty. There is no way to tell exactly when the data loads.
Carl Sawyer
I tried that from a button (so I knew the data was loaded) but couldn't get it to work). In fact, any reference to getNodeAt() prevents the page from compiling.
Laura
Use getTreeNodeAt() instead.
Carl Sawyer
My mistake (it was getting late here on the east coast). Even when we use the right method :) we don't get the expected results. For example:
alert(tree1.getTreeNodeAt(0).getProperty('data').value);
does return the proper value however:
alert(tree1.getTreeNodeAt(10).getProperty('data').value);
always returns nothing even through there are several hundred nodes in the tree. Any thoughts on why? Thanks.
Carl Sawyer
Anu
First of all, I must thank you for doing a great job at helping the developer community with CF Flash Forms. I am trying to pass a query object to a Tree at run-time using Flash Remoting and using the categories.dataProvider = results; only shows object tags. Do you know a way to render a Tree using a query returned from a CFC? I can do work around to format query to an XML object but that is a big overhead.
I would really appreciate your help with this. Thanks.
Carl Sawyer
myTree.selectedNode = myTree.getItemAt(i);
As a side note, Nahuel and Laura, your site has been extremely helpful in our work with Flash Forms. Thanks for all your advice.
Christopher
I've just started using Flash Forms/ActionScript and have found the discussions on AS Fusion very helpful. I'm building an application, which uses CFTree control to display a hierarchical menu structure (up to about four levels) on a Flash Form.
I want all the Tree nodes to be populated dynamically (except the very first level (parent) nodes, which I'm displaying initially on form load). I want to show the child menus when the user selects a node.
I'm having two problems with this approach:
(1) The first level (parent) nodes are shown as leaf nodes instead of as parent nodes since they had no children when they were first created, making it is difficult to programmatically change them to folders from my ActionScript when I've retrieved their children.
(2) Each time a node is selected its children are systematically duplicated and displayed under it using the code below. I don't want the children to be duplicated under the same parent folder.
// Find the menus under the selected menu item
function getChildNodes(itemID, parentID){
// Process the result we have received
getChildNodes_Result.onResult
= function( results: Object ):Void{
MenuTree.TreeDataProvider = results;
var index = MenuTree.selectedIndex;
for(var i = 0; i < results.length; i++){
if(index != 0){
// Add the node at index + 1
MenuTree.addItemAt(
index + 1, results.items[i].LABEL);
}
}
}
}
Any help will be gratefully appreciated.
Thanks
Chris
CarlosM()
I'm starting to use flex and no one on the flex side could answer.
I created a CFC that has a flash cftree and I want to send that object to flex. When I set the returntype to any and the <cftree format="object" in my CFC, I get the correct hierarchy in flex but the data shows only “ [object Object],1 ,,, null “.
There's got to be a way.
Christopher
My tree has up to five levels and I built in ColdFusion using a recursive custom tag to populate it when the page loads. Within my action script I use published Flex methods to add and remove tree items. This works fine except that I have the problem of not being able to refresh the tree after adding a node to the tree.
I think I have seen the behaviour you are describing before. Perhaps, I can offer some advice if you provide some code fragment to shwo what you rae doing?
Christopher
CarlosM()
This is my CFC:
<cffunction name="searchTree" output="false" access="remote" returntype="any">
<cfargument name="forInput2" default="11A005" type="string" required="no">
<!--Master Query end item with assemblies and components--->
<cfquery name="rsBill" datasource="fshift">
SELECT parent, component, insp_lt FROM bommaster WHERE (parentdist = '#arguments.forInput2#')
</cfquery>
<!---Parts with master as parent ---->
<cfquery dbtype="query" name="run1">
SELECT * FROM rsBill
WHERE insp_lt = 1
</cfquery>
<!---components showing child items --->
<cfquery dbtype="query" name="run2">
SELECT * FROM rsBill
WHERE insp_lt > 1
</cfquery>
<cfform name="form1" height="800" format="html">
<cftree name="searchTree" format="object">
<!---First run gives all parts and assemblies under masterItem --->
<cftreeitem value="#arguments.forInput2#">
<cfoutput query="run1">
<cftreeitem value="#component#"
parent="#arguments.forInput2#">
</cfoutput>
<!--- Second run loops db and places parts and components under their parent --->
<cfoutput query="run2">
<cftreeitem value="#component#"
display="#component#"
parent="#parent#">
</cfoutput>
</cftree>
</cfform>
<!-- return tree object --->
<cfreturn #searchTree#>
</cffunction>
</cfcomponent>
When I invoke this CFC I get the tree and all the data I need but with no label="part#". I get <node display="" value="" path="", but no label.
Now in Flex, if I set the dataProvider to the searchTree object that is pasted from the CFC, I get the structure but no data. Is there a way to send the dataProvider that cfform creates for the cftree in my cfc in ActionScript so I don't loose anything? If there's anything more I can tell you that would help please let me know?
Thanks again
Christopher
Sorry for the late reply.
I'm not entirely sure why you're not seeing the label but the problem may be the way you are populating your tree. Perhaps, you could try populating the parent nodes first, followed by the parts and components. Also, you may consider using cfloop instead of cfoutput because I've used cfoutput before with some limitations. Below is an example code I used before changing to using a recursive tag. This worked except that it wasn't as dynamic as I wanted.
Also, I found that using TreeDataProvider(an interface to hierarchical tree data and menu controls) is better than using dataProvider for a CFTree control.
Please note the placement of the loops (loops within a loop!):
<cfformgroup type="page">
<!--- An empty tree --->
<cftree name="MenuTree" format="flash">
<!--- Specify the root level for the tree as Menu --->
<cftreeItem value="Menu" expand="yes" img="fixed">
<!--- Loop over the parents query and display each top level menu --->
<cfloop query="GetParents">
<cftreeitem value="#GetParents.label#" parent="Menu" expand="no" img="folder">
<!--- Get the branches for each parent --->
<cfquery name="FirstLevelBranches" datasource="dev_flp">
SELECT * FROM crm_menu
WHERE PARENT = #GetParents.ID#
</cfquery>
<!--- Cycle through the branches and display them under their parent --->
<cfloop from="1" to="#FirstLevelBranches.recordcount#" index="i">
<cfif FirstLevelBranches.recordcount GT 0 >
<!--- It's a parent node --->
<cftreeitem value="#FirstLevelBranches.label[i]#" parent="#GetParents.label#" expand="no" img="folder">
<cfelse> <!--- It's a leaf node --->
<cftreeitem value="#FirstLevelBranches.label[i]#" parent="#GetParents.label#" expand="no" img="document">
</cfif>
</cfloop>
<!--- Get children of the first branches --->
<cfquery name="SecondLevelBranches" datasource="dev_flp">
SELECT * FROM crm_menu
WHERE PARENT = #FirstLevelBranches.ID#
</cfquery>
<cfloop from="1" to="#SecondLevelBranches.recordcount#" index="x">
<cfif SecondLevelBranches.recordcount GT 0>
<cftreeitem value="#SecondLevelBranches.label[x]#" parent="#FirstLevelBranches.label#" expand="no" img="folder">
<cfelse>
<cftreeitem value="#SecondLevelBranches.label[x]#" parent="#FirstLevelBranches.label#" expand="no" img="document">
</cfif>
</cfloop>
</cfloop>
</cftree>
</cfformgroup>
CarlosM()
So how do I pass the tree object in my CFC to populate a tree in flex or a tree in a cfm page?
This is killing me!!!
Chris
I'm not sure how you would pass the tree object in your CFC to populate the tree in flex.
In my previous reply I showed the code I wrote as a first stab for my project. I realised the approach wasn't quite dynamic so I used recursion to build a fully populated tree on form load.
Here is the code:
// From the Form, call the recursive coldFusion tag:
<!--- Insert the root level static tree item --->
<cftreeitem value="0" parent="0" expand="yes" display="Menu" img="Computer">
<!--- Generate the rest of the tree --->
// This is how to call the custom tag
<cf_recurse>
// This is the contents of the custom tag i.e. recurse.cfm
<cfquery name="GetParents" datasource="dev_flp">
SELECT *
FROM CRM_MENU
WHERE parent = #attributes.parentItemID#
</cfquery>
<!--- Loop through the GetParents query and create a tree for each parent --->
<cfloop query="GetParents">
<cfif GetParents.recordcount GT 0>
<!--- This must be a parent node --->
<cfset image = "Folder">
<cfelse>
<!--- This must be a leaf node --->
<cfset image = "Document">
</cfif>
<!--- Create the tree item --->
<cftreeitem value="#ID#" parent="#attributes.parentItemID#" display="#label#" img="#image#" expand="no">
<!--- Find children of the current parent --->
<cfquery name="GetChildren" datasource="dev_flp">
SELECT *
FROM CRM_MENU
WHERE parent = #GetParents.ID#
</cfquery>
<!--- If there is a child for the parent, call recurse again but this time make the parentItemID equal to the ID of the current child item. --->
<cfif GetChildren.recordcount GT 0>
// recursive loop
<cf_recurse
parentItemID = "#GetParents.ID#">
</cfif>
</cfloop>
To add a tree item I do:
responseHandler.save_Result = function( results: Object ):Void {
// Selected item will be the parent of the new item
var index = MenuTree.selectedIndex;
for(var i = 0; i < results.length; i++){
// check user has not selected the static tree item
if(index != 0){
MenuTree.addItemAt(index + 1, label);
// where label is the display value from the result object.
}
}
}
Similarly to update a menu item I do within the save_Result:
// This will replace the edited menu item
MenuTree.replaceItemAt(index, label);
Note: the only problems I have with this approach
is that I cannot refresh my tree to show the correct database values except I reload the page -not nice!
Hope this helps.
Chris
Chris
I have recreated your original problem i.e.
"When I set the returntype to any and the
<cftree format="object" in my CFC, I get
the correct hierarchy in flex but the data
shows only “ [object Object],1 ,,, null “.
As I stated in my second posting the reason
you are seeing [object Object],1 ,,, null “
is because you are doing something like
SearchTree.dataProvider = results;
This will definitely produce the behaviour you are seeing. I have tried in my code and produced the following:
[object Object],1 ,,[object Object], null
The proper thing to do I think is in responseHandler.onResult:
SearchTree.TreeDataProvider = results;
BTW: You said "I get the correct hierarchy in flex but the data shows only ..." Judging from what I've seen, it's probably not the hierarchy that you are seeing.
Any more problems just shout!
Chris
CarlosM()
Man, I wish I could go to MAX...
Well Chris, Thanks for giving me your time and effort. I really appreciate it.
Take it easy
Bradford
schwag
I tried doing it with a global style:
_global.styles.Tree.setStyle("openDuration", 600);
then also in line:
style="open-duration:600;"
Neither seem to work? am I doing something wrong in code or is this a CF server bug?
joel johnston
Thanks
Jeremy
[object Object],1 ,,[object Object], null
problems.
BTW, this site is great, we're just starting to fool with this fancy stuff and most of the questions I have are on here!
Laura
It is a little difficult to do if you don't know ActionScript. The easiest way is to loop over the query and create the xml as it is shown in the post example.
But to give you an idea, you would have to loop over the query in actionScript, adding each item to the root (or whatever node you want) by addTreeNode("My Label")
for (var i = 0; i < results.length; i++){
var node = parentNode.addTreeNode(results.items[i].Name, results.items[i]);
}
The code above assumes you have a variable called parentNode pointing to the root or node where you want to add the children. It also assumes your query has a column called Name.
It may not work as is for your case, it is just an idea of what you might do. ;)
That is what I use in the file explorer application if you want to see how I did it by sending a query, particularly the getDirectories_Result function
Hope that helps
Jeremy
tree1.getTreeNodeAt(0).href = "http://google.com";
tree1.getTreeNodeAt(0).target = "_blank";
did nothing (didn't break code, but didn't work either)
Also, once populated from Flash Remoting, I'd like the tree to open up at the root to show the level 1 leaves. Trying this didn't do anything:
tree1.setIsOpen(0,true,true,true);
Thoughts?
Jeremy
tree1.setIsOpen(tree1.getTreeNodeAt(0),true,true,true);
=)
How about getting rid of the border?
tree1.border = "no";
doesn't work.
Derek Bezuidenhout
Thanks for the great site!
Chris
if you want to get rid of the border, use the style attribute in the cftree tag:
<cftree style="borderStyle:none;" />
Jeremy
Tito Chatzis
casey
Here's what I've noticed:
The tree is filtering correctly, the grid is maintaining data that should be removed by the tree selection. I think this is occuring because of the way or organization is structured...
The Directors are also someone's immediate Manager.
The Tree is giving the appropriate filtered view, whereas the grid is showing the view for the Director in the lower branch rather than filtering on the Manager level.
Here's the code and hopefully a better explanation of the problem:
<cfquery name="users" datasource="#application.dsn.oiws#">
SELECT *, LAST_NAME + ',' + FIRST_NAME AS EENAME
FROM tbl_employee_master
WHERE VP = 'VP, Sally'
ORDER BY EENAME
</cfquery>
<cfform name="myForm" format="flash" height="355" width="700" skin="haloSilver">
<cfformitem type="script">
function applyFilter( term:String, grid:mx.controls.DataGrid, columns:Array ):Void {
var filterTerm:String = term.toString().toLowerCase();
if(filterTerm.length > 0) {
if(_global.unfilteredData[grid.id] == undefined){
if (_global.unfilteredData == undefined){
_global.unfilteredData = {};
}
_global.unfilteredData[grid.id] = grid.dataProvider.slice(0);
}
var filteredData:Array = [];
for(var i = 0; i< _global.unfilteredData[grid.id].length; i++) {
var item:Object = _global.unfilteredData[grid.id][i];
var added:Boolean = false;
for(var j = 0; j< columns.length; j++){
if(!added){
var value:String = item[columns[j]].toString().toLowerCase();
if(value.indexOf(filterTerm) != -1) {
filteredData.push(item);
added = true;
}
}
else {
break;
}
}
}
grid.dataProvider = filteredData;
}
else {
if(_global.unfilteredData[grid.id] != undefined) grid.dataProvider =
_global.unfilteredData[grid.id];
}
}
</cfformitem>
<cfformgroup type="panel" label="Tree View" height="345">
<cfformgroup type="VBox" Height="150">
<cftree
format="flash"
Name="tree1"
onchange="applyFilter(tree1.selectedNode.getProperty('data').value,data,['VP', 'DIRECTOR', 'MANAGER', 'EENAME'])"
Width="250"
HScroll="yes"
VScroll="yes"
Border="yes"
AppendKey="yes">
<cftreeitem value="VP, DIRECTOR, MANAGER, EENAME"
display="VP, DIRECTOR, MANAGER, EENAME"
query="users"
queryasroot="no"
expand="Yes,no,no">
</cftree>
</cfformgroup>
<cfformgroup type="panel" height="145" label="View For /{tree1.selectedNode.getProperty('data').value}/">
<cfgrid name="data" height="100" query="users" rowheaders="false">
<cfgridcolumn width="100" name="EMPLID" header="Employee ID">
<cfgridcolumn width="100" name="RACFID" header="RACF ID">
<cfgridcolumn width="100" name="LAST_NAME" header="Last Name">
<cfgridcolumn width="100" name="FIRST_NAME" header="First Name">
<cfgridcolumn name="ROLE" header="Role">
</cfgrid>
</cfformgroup>
</cfformgroup>
</cfform>
Example of bad data....
User clicks on the tree at the Director Level:
Tree and Grid display all employees that belong to that Director.
User clicks on the same Director, only at the Manager level of the tree:
Tree displays only employees that show this director as a manager.
Grid maintains the Director level view, showing both employees that reference this person as a Director and a Leader.
Hope that helps.... This is my first attempt at using a tree, so I've probably violated some obvious rule that's causing the grid to mess up.
casey
The problem is here:
onchange=
"applyFilter(tree1.selectedNode.getProperty('data').value,emplList,['VP', 'DIRECTOR', 'MANAGER' 'EENAME' ])
If there's a way for me to check where at in the node we are (node 1, 2, 3..) or even count how many \'s in the path exist... I could then say applyFilter....VP or VP, DIRECTOR, ETC....
Any help on how to incorporate javascript into the onchange event of the cftree would be appreciated... Here's what I've been working on (but cftree doesn't wanna play):
<script language="JavaScript">
function countscript()
{
aString = tree1.selectedNode.getProperty('data').value
temp = aString[:] # use slice to make a copy
count = 0
index = temp.find('\')
while index != -1:
count += 1
temp = temp[index + 1:] # use slicing
index = temp.find('\)
alert("I found %d occurrences of '\' in %s" % (count, aString));
}
</script>
Tito Chatzis
function callAction(thisNodeID)
{
document.tree.selectedNodeID.value = thisNodeID;
document.tree.submit();
}
</script>
<cfsaveContent variable="onClick">
var thisNodeID = nodes.selectedNode.getProperty('data').value;
var command = "javascript:callAction(" + thisNodeID + ")" ;
getURL(command);
</cfsavecontent>
<cftree name="nodes" format="flash" required="no" delimiter="," completePath="yes" appendKey="no" highlightHref="yes" lookAndFeel="windows" font="helvetica" fontSize="11" italic="no" bold="no" height="400" width="250" vSpace="0" hSpace="0" align="left" border= "no" hScroll="yes" vScroll="yes" enabled="Yes" visible="Yes" tooltip="Select Page" notSupported="Get Flash" onChange="#onClick#" style="borderStyle:none;">
<cftreeitem value="0" display="ROOT" parent="nodes" expand="yes">
</cftree>
casey
i did try and use some of that code, but the page refused to load for me...
right now i'm trying to determine the node(0), (1), etc....
ultimately i'm thinking something like this:
replace getTreeNode with whatever will tell me if we're at 0, 1, 2...etc...
if (tree1.getTreeNode(0))
{
applyFilter(tree1.selectedNode.getProperty('data').value,emplList, ['VP'])
}
else if (tree1.getTreeNodeAt(1))
{
applyFilter(tree1.selectedNode.getProperty('data').value,emplList, ['DIRECTOR'])
}
else if (tree1.getTreeNodeAt(2))
{
applyFilter(tree1.selectedNode.getProperty('data').value,emplList, ['MANAGER'])
}
else if (tree1.getTreeNodeAt(3))
{
applyFilter(tree1.selectedNode.getProperty('data').value,emplList, ['EENAME'])
}
else
{
applyFilter(tree1.selectedNode.getProperty('data').value,emplList, ['VP'])
}
Thanks again!
Sorry for being a noob!
Casey
jeff
In the past I have used the following code without remoting:
tree.selectedNode.getProperty('data').value
Now that I am remoting the tree from a query I can't use this anymore. I was thinking that maybe the proper way to modify this code is by doing the following:
//When I made the node I gave it the following properties:
for (loop.....)
Data = results.getItemAt(i);
tree.addTreeNode(Data.Title, Data.ActivityID);
//on selection I run this code:
tree.selectedNode.getProperty('ActivityID')
I tried many combinations but I haven't been able to get the correct data. Does anybody know how to reference this?
Thank you for your time!
Jeff
Jeff
I did the following to find the data:
Programs.getNodeDisplayedAt(Programs.selectedIndex).getProperty('data')
Hopefully this might help somebody.
shawn gibson
I don't get the sense this project here would be able to be re-jigged for that use, would it? It seems to actually only be relevent for CF pages, not Flash files per se...as with much of the CF code available for use with Flash. Am I correct?
I'm not certain how the connectivity all works yet, trying to grasp remoting, xml, CF, flex and db concepts all at once.
I can certainly use this type of page you have here, in CF, for admin purposes, but I'd be very happy if I could actually build this type of thing directly into my Flash projects...Any suggestions?
Shawn
Peter
<rootNode display="something">Some text
<parentNode display="something">Some text
</parentNode>
</rootNode>
I know how to address this attribute data (here = "something"), but how to address this "Some text" data?
Thanks in advance
Prema
I am using CFMX 7. I have a CFTREE built from a query. Would you please help me with the following :
1. How do I check, if the value of a node matches with the value passed thru URL?
2. Tree.getTreeNodeAt(0).getProperty('data').value gives the value of the root. But how to get the value of the child nodes? Is there any function available?
Thanks in advance.
Prema
Jeff
Alain
A late contribution to this site, which has with no doubt been the most helpful source of my whole professional life...
It looks like the question of pre selecting a particular node once the tree has been loaded has remained open. I have written a piece of code to do so, which I would like to share here. Although I am aware this technology seems obsolete now everyone has moved to Flex & more, I still hope it could help somebody... or am I the only one still using CF8 and Flashforms?!
The idea is the following: Once the data has loaded, the tree is fully opened, then looped from the bottom upwards and the nodes are closed or not, depending on whether they hold the leaf that needs to be preselected. The code to open all nodes has been borrowed from www.sustainablegis.com/blog/cfg11n/index.cfm?mode=entry&entry=FC680842-20ED-7DEE-2A2E14FBEFD0601A
My XMl uses 2 special attributes: rank (anything you want...) and depth, which increases for each sub node. Here we go...
public function getmynode(mynodename:String, mynodevalue:String):Void {
//open everything
var theTree=categories; //cftree name
var i=0;
var mydepth=0;
var thisNode=theTree.getTreeNodeAt(i);
while (thisNode != undefined){
if (theTree.getIsBranch(thisNode) && ! theTree.getIsOpen(thisNode)){
theTree.setIsOpen(thisNode,true);
}
i++;
thisNode=theTree.getNodeDisplayedAt(i);
}
//loop from the bottom of the tree upwards)
for ( var j=i; j>-1; j-- ) {
//find out if j level is a branch
if (theTree.getIsBranch(theTree.getNodeDisplayedAt(j)) && theTree.getIsOpen(theTree.getNodeDisplayedAt(j))){
//it is an open branch so close it
theTree.setIsOpen(theTree.getNodeDisplayedAt(j),false);
}
//It is a leaf
else {
//check if rank is mynodename
if(theTree.getNodeDisplayedAt(j).getProperty('rank')==mynodename){
//ok. The rank is right. Now check the id
if(theTree.getNodeDisplayedAt(j).getProperty('id')==mynodevalue){
//ok we have found it. Now we need to exit the loop...
mydepth=theTree.getNodeDisplayedAt(j).getProperty('depth');
categories.selectedNode=categories.getItemAt(j);
break;
}
}
}
}
//we need to go up and close the right branches
for ( var k=j; k>-1; k-- ) {
//check it is an open branch
if (theTree.getIsBranch(theTree.getNodeDisplayedAt(k)) && theTree.getIsOpen(theTree.getNodeDisplayedAt(k))){
//the first branch at a shallower level stays open
if(theTree.getNodeDisplayedAt(k).getProperty('depth')< mydepth) {
//branch stays open. just decreases the depth.
mydepth --;
}
else {
//close this branch
theTree.setIsOpen(theTree.getNodeDisplayedAt(k),false);
}
}
}
}
That's it! It's neither pro nor clean code, cause I am not... but it works! However, it seems to be a bit slow on a tree with 10000 records, so any suggestion for improvement would be appreciated.
Alain
Scott
Can you share an example of your XML. The tree is closing Completely when I use your script.
Thanks.
Alain
I am away from the computer for a while, so won't be able to post more before a few weeks. This is a quick post from my Iphone, in Slovenia.
I believe the problem you mention comes from your XML. The tree closes completely because it does not find a node to open that meets the criteria. Your XML categories should have the following attributes: depth, id and rank. Then, make sure that in the XML you return, there is a node which has the value mynodename for rank and mynodevalue for id.
The code I am using is exactly what I have posted. The only thing I could post is the XML in the remote call, but won't be able to do that before at least a week.
Let me know if you managed to make it work.
Alain
Alain
Am back... This is an example of the code I use to populate the tree. It loads a list of names (students), sorted first by initials, then last name and first name.
<cfset var categories = ""/>
<cfquery name="initialqry" datasource="mydatasource">
SELECT DISTINCT upper(left(lastname,1)) as initial FROM students
order by lastname asc
</cfquery>
<cfxml variable="categories"><categories label="Students List" id="Students" rank="" depth="0"><cfoutput query="initialqry"><category id="#initialqry.initial#" label="#initialqry.initial#" rank="initiale" depth="1"><cfquery name="nameqry" datasource="mydatasource">SELECT DISTINCT lastname FROM students where left(lastname,1) = "#initialqry.initial#" order by lastname asc</cfquery><cfloop query="nameqry"><category id="#nameqry.lastname#" label="#nameqry.lastname#" rank="lastname" depth="2"><cfquery name="firstnameqry" datasource="mydatasource">SELECT FirstName, StudentID FROM students where lastname = "#nameqry.lastname#" order by FirstName ASC</cfquery><cfloop query="firstnameqry"><category id="#firstnameqry.StudentID#" label="#firstnameqry.FirstName#" rank="StudentID" depth="3"></category></cfloop></category></cfloop></category></cfoutput></categories></cfxml>
<cfreturn categories />
I could then preselect the student whose StudentID is 100 by calling getmynode('StudentID', 100)
I hope that helps. Alain