How To Filter An ArrayCollection That Is The Data Provider For A Flex 2 Tree Control

When you use an ArrayCollection or XMLListCollection to provide the data for a Flex Tree control, you can filter the items that are displayed. This example shows one way to filter an ArrayCollection that is used as the data provider for a Tree control. For background, see my previous post about using an ArrayCollection as the data provider for a Tree control and also how to filter an ArrayCollection based on multiple criteria.

View an example Flex application (right click on the application to view the source code). Be patient as it may take 15-20 seconds for the application to load the data. (I'm working on getting the data into Flex faster.)

My previous version of the Tree control displayed hierarchical data about consultants, their areas of expertise, and their consulting roles. In this version of the Tree control example, I added two combo boxes that will assist the user in filtering the items displayed in the Tree control. The first combo box provides the areas of expertise for the consultants and the second combo box provides the roles a consultant can perform. So if the user wants to see only those consultants with a specific area of expertise in the Tree, the user selects that area in the combo box. If the user wants to see only those consultants with a specific expertise and a specific role for that area, the user makes a selection in both combo boxes.

When the user makes a change to a combo box, function updateTree is called.


//called when combo box selection changes

private function updateTree():void {
    
     //cause the ArrayCollection to apply the filter
        
        consultantAryCol.refresh();
        
        consultantTree.selectedItem = null;
        
        //resetting the data provider will cause the tree to redraw
        
        //and show only the items that were filtered
        
        consultantTree.dataProvider = consultantAryCol;
    
}//end function updateTree

The call to refresh() causes the ArrayCollection to call the filter function (which I previously set earlier to the function named treeAryColFilter) for each item in the ArrayCollection. If the filter criteria is found in an item, the filter function returns true and that item is displayed in the Tree. By resetting the Tree's data provider, the Tree control is forced to redraw its items and since the ArrayCollection has been filtered the Tree will only display the items that matched the filter criteria.

The filter function for a Tree's ArrayCollection is slightly different than for a DataGrid's ArrayCollection. The ArrayCollection for the Tree is an array of Objects. Each object also contains an array of objects and each of those objects also contains an array (see my previous post about using an ArrayCollection as the data provider for a Tree control for the ArrayCollection's make up).

In the ArrayCollection's filter function (treeAryColFilter), I use a series of if statements to figure out which combo box the user changed (or if the user has selected an item in both combo boxes). In each if statement I call a separate function that will return true if the value the user selected in the combo box is in a tree item. For example, if the user selected "artificial intelligence" in the areas of expertise combo box, I call the searchForArea function. This function searches through every Object stored in the children attribute of the item in the tree and returns true if a match is found.


//search for the areaID

private function searchForArea(consultant:Object, areaID:int):Boolean {

    var found:Boolean=false;
    
    for each (var area:Object in consultant.children) {
        
        if (area.areaID == areaID) {
        
         found = true;
         break;
        
        }//end if
        
     }//end for
    
     return found ;

}//end function searchForArea

The point here is that you must loop over each object stored in the array (consultant.children) and compare the object's attribute (area.areaID) value to the value the user selected in the combo box (areaID). The for each statement loops over each Object stored in the consultant.children array (children is the attribute in the consultant object that is an array of Area objects).

Filtering a large number of items displayed in the Tree can assist the user in quickly finding the data she wants without having to manually go through all the items.

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Hi, is it possible to filter the tree without the nodes collapsing?
# Posted By Alex MacCaw | 3/11/07 12:25 PM
I don't think so, since the tree control is completely redrawn when the filter is applied. Since you don't know what the user will look for, you won't know the results obtained by applying the filter. Thus, you won't know what tree items should be opened.
You may be able to add some complex programming to keep track of which tree items are opened and if those same items appear again after the filter is applied you could have them open also.
# Posted By Bruce | 3/11/07 1:14 PM
Hey Bruce,

Great post... this really came in handy!
# Posted By Rich | 1/15/08 10:40 AM
Hi Bruce,
Thanks a lot for a good article.
But here is a problem i am facing now trying to filter DataProvider based on a click event's x_coordinate:

private function DestinationsResultHandler(event:ResultEvent):void {
destinationsList = event.result as ArrayCollection;
trace(destinationsList);
}


[Bindable]
   private var currentClickedItem:String;   
   public function getClickItem(event:Event):void{
var currentClickedItem=event.currentTarget.getRepeaterItem().x_coordinate;
            trace('coordinate '+currentClickedItem);
         }

public function filterDG(item:Object):Boolean{
            var result:Boolean=false;
            trace(currentClickedItem);
            if(item.x_coordinate==currentClickedItem){
               result= true;
            }
            return result;

         }

public function filterAC():void {
destinationsList.filterFunction=filterDG;
/* Refresh the collection view to apply the filter. */
destinationsList.refresh();
test_dg.dataProvider=destinationsList;
}



<mx:Canvas id="map_objects" x="0" y="0">
   <mx:Repeater id="destinations_list" dataProvider="{destinationsList}">
   <mx:Image x="{destinations_list.currentItem.x_coordinate}" y="{destinations_list.currentItem.y_coordinate}" toolTip="{destinations_list.currentItem.title}" width="30" height="30"
   source="../assets/logo_btn.swf" id="location_image" click="getClickItem(event),filterAC()"/>
   </mx:Repeater>
   </mx:Canvas>

It gives me errors, and i cant make any filtering, because it somehow returns null as x_coordinate in filter, although in trace of getClickItem i can see proper number.

What i am doing wrong? Please help!
# Posted By Mika | 4/15/08 4:48 AM
I made it. Here is how:

   [Bindable]
         private var currentClickedItem:String;
         
         public function getClickItem(event:Event):void{
            var currentClickedItem=event.currentTarget.getRepeaterItem().x_coordinate;
            trace('coordinate '+currentClickedItem);
         }
         
         public function filterDG(item:Object):Boolean{
            
             if (currentClickedItem==null)
return true
else
return item.x_coordinate == currentClickedItem;
            
         }
      

         
      public function filterAC(event:Event):void {
currentClickedItem=event.currentTarget.getRepeaterItem().x_coordinate;
destinationsList.filterFunction=filterDG;
/* Refresh the collection view to apply the filter. */
destinationsList.refresh();
test_dg.dataProvider=destinationsList;
}
# Posted By Mika | 4/15/08 11:14 AM
Hi Bruce,
Thanks for explaining an important issue.
I still find the tree filtering a bit cumbersome. As far as I understand it, I need each node of the tree to hold all the information needed for filtering. This means that I can't just filter by the leafs of the tree - I need the parents to be aware if their children are filtered or not. If not - a parent may be filtered out and then all of its children will be gone even if they hold a needed data.
Is there a way to tell flex to filter only according to leaves?
# Posted By Yoav | 6/16/08 12:33 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.1.002. Contact Blog Owner