Flex 2 provides a Tree control that can be used to display hierarchical data. The examples of using the Tree control in the Flex 2.0 Developer's Guide use XML as the data provider. However, I needed to populate a Tree control using data pulled from a database. The data provider for a Tree Control can be either XML (including an XMLList and XMLListCollection), an array of items, an object that contains an array, or an ArrayCollection. I decided to use an ArrayCollection rather than XML as I figured it would be simpler to create the ArrayCollection using data from my database rather than create the complex XML.
However, I found very few examples of using an ArrayCollection as the data provider for the Tree control. So after I figured out how to get the ArrayCollection to work, I decided to post what I learned to hopefully help others. A demonstration application is located here: /flex/consultantTree/consultantTree.html (right click on the app to view the source).
- Flex 2 Developer's Guide - Tree Control; http://livedocs.macromedia.com/flex/2/docs/00000600.html#397087
- Flex 2 Language Reference - Tree Control; http://livedocs.macromedia.com/flex/2/langref/mx/controls/Tree.html
- Working With Tree Controls, http://www.adobe.com/devnet/flex/quickstart/working_with_tree/
- Using a Hierarchy of Components to Populate a Flex Tree Control,
- Building Flex Tree MXML From ColdFusion Queries, Ben Forta,
http://www.forta.com/blog/index.cfm/2006/8/24/Building-Flex-Tree-MXML-From-ColdFusion-Queries (note this reference provides an example of creating complex XML using results from a database query)
Creating The ArrayCollection
To better follow the discussion of the ArrayCollection used as the Tree's data provider, you may want to open the demonstration application and play with it. Seeing the data displayed in the Tree may be helpful in understanding what makes up the ArrayCollection.
My Flex application calls a CFC function named getAllConsultants, which is in the ConsultantGateway CFC. This function returns an array of Consultant objects (see Consultant.cfc). A Consultant has several attributes, one of which is named children. The children attribute is an array that stores Area objects (see Area.cfc). Areas represent a consultant's areas of expertise. A consultant will have 1 or more areas of expertise. So the children attribute of the Consultant CFC stores all the Area objects for that consultant.
Why is the attribute named children? Good question. If you review pages 415-416 of the Flex 2 Developer's Guide, you will see that the object that contains an array of items that you want to display in the tree, must be named children. So as the Tree control processes each Consultant object in the ArrayCollection, it will create a branch under the Consultant for each Area stored in the Consultant's children attribute.
Each Area object stored in the Consultant's children array also has an attribute named children that is an array. Stored in this children array are one or more Role objects (see Role.cfc). For each area of expertise a consultant has, the consultant is willing to perform one or more different roles. As the Tree control processes each Area, it will create a branch below the Area for each Role stored in the children object.
So the array returned by the function getAllConsultants has a hierarchy of objects: Consultant - Array of Area objects (named children) - Array of Role objects (named children). I use this array to create the ArrayCollection that is the Tree's data provider (see function handleGetAllConsultants in the consultantTree.mxml file).
Side Note: When you are going to return CFC objects to Flex, make sure you specify a CFProperty tag for each CFC attribute. In the CFProperty tag be sure to set a value for type.
Creating The Tree Control
The ArrayCollection is specified as the Tree control's data provider. Additionally, I needed to use the labelFunction attribute of the Tree control. This attribute specifies a function to call to determine what to show as the "label" in the Tree control. If you examine the treeLabel function you'll notice that I have a series of if statements to determine which object field's value to return for the label. When the Tree control calls this function it passes an Object representing the current branch or leaf. So if this object has a fullName field (the fullName field is not null), then the value stored in the fullName field is what should be used for the label, else if the object has a description field then use the value stored in the description field for the label.
Manipulating The Tree Control
The ArrayCollection is also the data provider for a DataGrid control. When the user selects a row in the DataGrid, Flex calls the dgChangeHandler function. In function dgChangeHandler I use a property and a method of the Tree control to change the Tree control's appearance.
The Tree control property firstVisibleItem can be used to set which Tree item is displayed in the top row of the tree. You set this property equal to the object of the ArrayCollection that you want to be the top row of the Tree. I just use the ArrayCollection's getItemAt method to get the item the user selected in the DataGrid.
The Tree control method expandItem is used to open a branch so its children are visible. This method takes an argument that identifies which item in the ArrayCollection represents the item the Tree control should expand.
After completing this work, I think it may have been easier to have ColdFusion create the XML rather than a complex array of objects. Having to use "children" for the name of an attribute of my CFC just makes me uncomfortable. I want my attribute names to represent what is being stored (eg lastName, email). I did like being able to leverage my CFC objects as part of the solution.
My reference number 5 above (Ben Forta's blog entry) describes how to create a complex XML file from a flat database query result. His solution may be something I try the next time I need to populate a Tree control using information extracted from a database.
Additionally, my solution uses too many queries and creation of CFC objects. If I had several hundred consultants, the time lag it would take to get the data into Flex would not be acceptable. Right now I am only pulling in 40 consultants (there are approximately 200 queries and 800 objects being created to get all the areas and roles for these consultants) and it is taking approx. 15-30 seconds to pull the data from CF into Flex. Clearly, my solution is not going to scale well.
Lastly, there needs to be much better documentation for the Tree control. This control is one of more complex ones that Flex offers. However, few of the Tree control's properties and methods are explained well and there are only limited examples. I never could figure out how to close a Tree item (yes I see the documentation states that expandItem can close a branch, but I never got that to work.) Hopefully, the Flex books that will be published in 2007 will provide more complete examples of using the Tree control. If you know of a good Tree control example, please post a comment.