The random utterances of David Arno

How to filter all nodes of a Flex Tree component

Flex logoIf you have ever tried to display filtered data in a Flex component, you’ll likely be aware that you can use an XMLListCollection object as the data source and that you can apply a filter function to that collection. The component only then displays the filtered data. However if you have tried this with a Tree component, you’ll have discovered that it doesn’t work. The filter is only applied to the top-level node(s) in the collection. The filter isn’t recursively applied to child nodes.

There is a an open bug related to this problem in the Flex Bug and Issue Management System. It is currently targeted to be fixed for Flex 3.4.0. In the meantime though, there is a simple work-around that I’ll describe here.

When rendering its nodes, a Tree object relies on a helper class that implements one of the ITreeDataDescriptor or ITreeDataDescriptor2 interfaces. By default it is an instance of DefaultDataDescriptor, but one is free to supply the tree with a different implementation. By overriding the tree’s data descriptor with one that supports filtering, the tree’s content can be easily filtered. The simple Flex SWF below demonstrates this point. Try selecting/ unselecting the checkboxes on the right and you’ll see that the tree’s content changes accordingly.

This movie requires Flash Player 9

The key to how it works is that the tree calls the data descriptor’s getChildren() method for each node. The filter function can therefore be called at this point and we only return to the tree the set of children that we want displayed.

My implementation of the ITreeDataDescriptor is far from complete. I have only implemented those methods needed to demonstrate the point, though it implements the required functionality just fine. The DefaultDataDescriptor is more complex, as it implements all the methods, plus it uses some clever caching mechanisms. I may implement a full replacement class at some stage that will support drag and drop for example, but for now that is left as an exercise for any reader who needs such functionality.

You can download the full FlexBuilder project source code for the above SWF here.


Share This Post...
16 comments so far, click here to read them or add another

16 Comments so far

  1. Shawny April 2nd, 2009 00:39

    Great post. I also (unwittingly) found the bug in using the XMLListCollection and filtering the subnodes when passing to a filterFunction. I plan to look over your solution in more depth tonight. Will the same approach work when you have subnodes of subnodes and want to filter on a value in the n-th subnode? For example:

    .....

    etc etc…

    Would this approach allow me to search for all products that are bright red AND expensive? Or something along these lines? See, Im interested in using XML to be able to filter subnodes by various filter values in order to be able to allow my users to have a variety of filters available.

    Cheers!
    Shawn

  2. Shawny April 2nd, 2009 00:44

    Well none of my xml was displayed… basically it looked like

    root product name="ball" price value="24.95" color value="green" purchases date value="2/2/2009" date value="5/7/2009" product name="yoyo" price value="10.95" color value="bright red" purchases date value="2/2/2009" date value="5/7/2009" ... root

    So if I wanted to find all products purchased in a date range and also cost more than $10 and were named yoyo. This is the type of solution Im trying to find. When your data is packaged so that there are multiple n-subnodes and those subnodes all contain subnodes that also contain data… etc Thanks!

  3. Shawny April 2nd, 2009 00:45

    Just as a note, Im trying to be able to filter chart data and not necessarily a tree.

  4. Jason April 4th, 2009 20:03

    This script was a life saver. (Cuz If I would’ve spent another night working until sun-up, my fiancee would’ve killed me) As is, it works great for 1 level, but I needed to filter a tree with an unknown number of sub-branches as a user types in text into a textInput object. I finally came up with a working filter function and wanted to share that here:

    <mx:TextInput id="filterMenuInput" change="if(filterMenuInput.text){openAllTreeNodes(menuTree); menuTree.invalidateList();}else{closeAllTreeNodes(menuTree);}"/>

    private function filterMenuTree(node:Object):ICollectionView{
    var str:String = filterMenuInput.text.replace(/\s/g,"\\s");
    var re:RegExp = new RegExp(str, "i");
    return new XMLListCollection(node.*.(@label.match(re) || descendants().(@label.match(re)).length()>0));
    }

    private function openAllTreeNodes(tree:Tree):void {
    tree.openItems = tree.dataProvider[0].parent().descendants();
    }

    private function closeAllTreeNodes(tree:Tree):void {
    tree.openItems = [];
    }

  5. Gerry McLarnon April 9th, 2009 15:24

    Thanks for that. I thought there might be a recursive filtering bug on XMLListCollection as I came across a recursive sorting bug in XMLListCollection yesterday. I wish I had used an ArrayCollection! I believe they don’t suffer the same problems.

    In a style similar to your solution I overrode the getChildren() method in a subclass of DefaultDataDescriptor to apply a sort and filter function to the children collection. Which seems like an elegant workaround for a quite annoying bug…

  6. Alex June 28th, 2009 10:53

    Thanks for the idea.

    I made a child from DefaultDataDescriptor with two overwritten functions:

    override public function getChildren(node:Object, model:Object = null):ICollectionView {
    if (filter == null)
    return super.getChildren(node, model);
    return filter(node);
    }

    override public function isBranch(node:Object, model:Object = null):Boolean {
    return getChildren(node, model).length > 0 ;
    }

    It fixes appearing of the open triangles by parents of the filtered nodes

    P.s.: It’s only for XML, of course.

  7. [...] data. Il avait été prévu de le corriger pour un éventuel Flex 3.4 puis il a été retiré. Certains ont donc trouvé de bonne solutions pour contourner ce problème, voici son [...]

  8. Gaurav May 6th, 2010 05:51

    Thanks for the example. Helped me save time and energy researching filtering tree nodes.

    For those who need to enable drag behaviour, just need to implement

    public function getData(node:Object, model:Object=null):Object { return node; }

  9. Yoav May 11th, 2010 08:30

    One thing bothers me about the solution: each time getChildren is triggered, a new XMLCollection is created. Isn’t this a memory issue? Aren’t we creating here a memory leak?
    In such cases I prefer to cache the resulted array or something like that. Any simpler idea?

  10. David Arno May 11th, 2010 12:28

    @Yoav,

    Not sure about that. I’ll have to investigate and get back to you in a couple of days.

  11. jac June 13th, 2010 10:02

    i have a question.

    If i have several childrens, how could i filter that? because, now only the first children level is filtered.
    My tree has 3 levels of hierarchy, and i need filter the 3rd level. (node 3)
    node 1 -
    node 2 –
    node 3.

    Anu idea? Thanks in adevance

  12. Eric February 11th, 2011 01:09

    hi,

    i have implemented the tree filtering without any problems, but i am having difficult time getting rid of first level node.

    for example if u tick off Red and Green –> the Yellow node would disappear instead of changing to a different Icon.

    thanks.

  13. Arthur July 21st, 2011 23:59

    I am developing a similar example and had a problem with scrollbar’s parent container. When I expand the tree, it creates the verticalscroll, but if I filter the tree to have a few nodes to disappear scrollbar, the scrollbar remains the same size, and if I scrolling, happens some issue in the layout tree ..

    I tried to call invalidateDisplayList in parent’s container but it did not fixed the problem.

    any suggestions?

    Thanks

  14. Suneel August 30th, 2011 10:35

    There is bug , when u filter with keyword that is not there in a xml, it goes blank , please suggest a solution

  15. Gerardo November 3rd, 2011 23:18

    Thanks a lot for this post. It took me a while to find a solution for my problem. :)

  16. Eason October 14th, 2012 09:50

    hi Arthur,
    the workaround is to force your tree open and close once when you filtered your tree. try add:

    yourtree.expandItem( yourtreedata,false);
    yourtree.expandItem( yourtreedata,true);

    I am using 4.6 sdk, but seems the bug still exists. Won’t it be solved in version 3.4? Anyway, thanks to David, it still helps after so many years.