Filtering of Hierarchical Data in AdvancedDataGrid

Filtering of Hierarchical Data in AdvancedDataGrid is quite straight forward.

Assign the filter function to the ADG’s dataProvider and call refresh() –

// assign filter function to
//the AdvancedDataGrid's dataProvider
IHierarchicalCollectionView(
        adg.dataProvider).filterFunction =
                                 myFilterFunc;

// refresh the ADG's dataProvider
IHierarchicalCollectionView(
        adg.dataProvider).refresh();

Here is a Sample

And the Source

68 thoughts on “Filtering of Hierarchical Data in AdvancedDataGrid

  1. Hello, interesting post but I tried to filter real world data and failed 🙂

    Is it is easy because there are only “mx” strings in it ?

    How do you filter something more complex, for example :

    private var fs2:Object =
    {fileName:”something”, children: [
    {fileName: “more”, children: [
    {fileName: “complex”, size:”5563 bytes”, lastModified:”October 6, 2006″}, …

    thank you in advance

  2. The filtering shown in the sample will apply to the whole collection recursively.

    In your case, filtering can be applied to a children collection of a node.
    You can fetch the children collection using –

    var children:ICollection = IHierarchicalCollectionView(
    adg.dataProvider).getChildren(node);

    And apply filtering on the children collection.
    In this way, you can apply different filtering to different children collections.

  3. Sameer, I’m new and I don’t understand your last post. Can you elaborate? Does the children:ICollectionView go in the filterfunction?

    Thanks

  4. Let me take an example. Suppose you have –
    A
    -A1
    -A2
    B
    -B1
    -B2

    Now, instead of applying the filterFunction to the entire collection you want to fetch the children of B and apply filter.
    When you fetch the children using –

    var children:ICollection = IHierarchicalCollectionView(
    adg.dataProvider).getChildren(node);

    A collection will be returned and you can apply sort/filter to that collection.

    More information on general sorting/filtering can be viewed here –
    http://livedocs.adobe.com/flex/3/html/help.html?content=about_dataproviders_4.html#441147

  5. Looked all over the web for this. Very simple and elegant. Anything similar for the Tree control? Tried iterating filterFunction in a Tree control with limited success. So converted to a single column AdvancedDataGrid and got it all.

    Well done.

  6. Sameer, how about if you wanted filter all A, B, A1, A2, B1, and B2 using the same filterfunction?

  7. How do you use custom item renderer with hierarchical data in an AdvancedDataGrid?

    I can use custom item renderer with flat data in an AdvancedDataGrid by creating a new class and extending AdvancedDataGridItemRenderer. But when the data is hierarchical it does not work.

    Is there some other class I need to extend for hierarchical data?

  8. I just found your post on a 3-state checkbox and figured it out from that code. Thank you!!!

    You have to use groupItemRenderer and create a new class and extend AdvancedDataGridGroupItemRenderer.

    Awesome!

  9. If you want to change the renderers at the group level, set the groupItemRenderer property in your grid. You can extend from AdvancedDataGridGroupItemRenderer.

    And for all the other renderers, there is AdvancedDataGridItemRenderer.

  10. In your example in the May 9th post:
    (Let me take an example. Suppose you have –
    A
    -A1
    -A2
    B
    -B1
    -B2)
    You have: var children:ICollection = IHierarchicalCollectionView(
    adg.dataProvider).getChildren(node);
    How do you specifiy that you want the node B. I need to get the count of children in a specific GroupingCollection in my AdvancedDatGrid and then programmatically open the node if it is 1. Your help is very much appreciated! Please email me. Thank you!

  11. The sample that I’ve given in this post filter all the nodes.
    Or do you want a sample in which filtering is applied to only a particular node and its children?

  12. Got tricky when applying a text based filter based on datetime field from SQL Server…

    item is returning a value of standard SQL Server datetime format…

    the user is filtering on date such as ‘7/1/2008’ as text…

    data shows different format and is filtering on that value.. not what is shown…

  13. If you want to show the leaf nodes also when you type in the Owner, the filter function needs to be changed. Suppose, if you type “work”, it should show all the nodes, right??
    Basically, you need to check for each parent if there is a valid child which contains the typed string and then return true/false.

  14. I tried filtering against my advanced data grid and it works great with the only difference that when I finish searching the children under my parent item are not expandable.

    Any ideas how I can fix that???

    Thanks in advance.

  15. Here is my solution simply my children are IP’s so I just looked for all ips and that did the trickery….

    private function searchParams(item:Object):Boolean{

    var isMatch:Boolean = false;
    var regEx:RegExp = /^([1-9][0-9]{0,2})+\.([1-9][0-9]{0,2})+\.([1-9][0-9]{0,2})+\.([1-9][0-9]{0,2})+$/;

    if(item.displayValue.toUpperCase().search(txtLookUp.text.toUpperCase()) != -1 || item.displayValue.search(regEx) != -1){
    isMatch = true;
    }

    if(this.reportType == ReportType.ByIP)
    {
    //DEBUG IP FIELD MAY BE DIFFERENT
    if(item.ip.toUpperCase().txtLookUp(txtLookUp.text.toUpperCase()) != -1){
    isMatch = true;
    }
    }

    return isMatch;
    }

  16. Thanks for the code sample. There’s 1 problem that doesn’t appear in your sample, but does with real world data. That’s when a child node has content that a parent node doesn’t. In that case, if you have a node 2 levels deep that matches, it doesn’t show up because the parent node is hidden. Here’s my solution to that problem:

    private function browseFilter(item:Object):Boolean {
    var xml:XML = XML(item);
    if(xml.children().length() > 0){
    var hasValidChildren:Boolean = false;
    for each(var node:XML in xml.file){
    if(browseFilter(node)){
    hasValidChildren = true;
    break;
    }
    }
    return hasValidChildren;
    }
    else {
    return String(xml.@path).indexOf(view.browseSearch.text) != -1;
    }
    }

    This will do the actual recursion for each node, and hide it if it doesn’t have any matching children.

  17. my datagrid generates a chart.i want to plot another chart based on the child nodes of my datagrid,which should come on clicking a bar.
    eg.
    chart1:
    columnchart dataProvider=”{dg.dataProvider}” itemClick=”DrillDown(event)”

    chart2:
    columnchart dataProvider=”{AC}”

    AC should contain the data of the child nodes of a group(that is represented as a bar in chart1)

    hope i stated my problem clearly,please suggest your solution.
    Thanks in advance

  18. When using the Data Management Service with a one-to-many lazy associated data in a HierarchicalData dataProvider, when I apply any filter function other then null, newly opened entities appear twice. This is a serious bug in FDS…

  19. I was trying to use XML as dataprovider for advancedDataGrid. I tried using Steven’s code. It works like a charm, only that, when I do the search, it only searches child nodes. I want something which can search both parent and child nodes. I have tried
    if((ObjectUtil.toString(item)).indexOf(searchText.text)!=-1)
    nbsp;nbsp;nbsp;return true;

    return false;

    this shows up parent, but nothing shows up when I try to expand it. Any updates on this is greatly appreciated

    • Sounds like I’m running into a similar issue that Ram reported on Feb19. The filtering works great for child nodes, but children of matches get filtered out.
      Example:

      All Cars
      Domestic Cars
      Lincoln
      Navigator
      MTX
      Foreign Cars
      BMW
      X5
      M3
      Mercedes
      GL
      SL

      So, if I filter on “Mercedes” in this sample, I only get

      All Cars
      Foreign Cars
      Mercedes

      where I need to display

      All Cars
      Foreign Cars
      Mercedes
      GL
      SL

      Likewise, if I enter “All” I’d like to get the entire tree instead of just

      All Cars

      It seems that the filterFunction applies to each node individually, making them get filtered out of the tree if they don’t match the search pattern. Any suggestions on how to include all children of matching nodes?

      -tri

      • Reposting the example tree, hopefully preserving indentation an hierarchy this time.

        All Cars
          Domestic Cars
            Lincoln
              Navigator
              MTX
          Foreign Cars
            BMW
              X5
              M3
            Mercedes
              GL
              SL

  20. i was trying to use apply filter to ADG with HierarchicalData provider but when I try to refresh dataprovider an action script error occurs:

    Error #1009: Cannot access a property or method of a null object reference.
    TypeError: Error #1009: Cannot access a property or method of a null object reference.
    at mx.collections::HierarchicalCollectionView/internalRefresh()[C:\Work\flex\dmv_automation\projects\datavisualisation\src\mx\collections\HierarchicalCollectionView.as:700]
    at mx.collections::HierarchicalCollectionView/refresh()[C:\Work\flex\dmv_automation\projects\datavisualisation\src\mx\collections\HierarchicalCollectionView.as:681]
    at views.questionary::QuestionaryExplorer/filterADGHD()[/usr/local/FilterSample/src/FilterWins/FilterWin1.mxml:438]
    at FilterWins::FilterWin1/__searchTextADGHD_change()[/usr/local/FilterSample/src/FilterWins/FilterWin1.mxml:601]
    at flash.events::EventDispatcher/dispatchEventFunction()
    at flash.events::EventDispatcher/dispatchEvent()
    at mx.core::UIComponent/dispatchEvent()[E:\dev\3.0.x\frameworks\projects\framework\src\mx\core\UIComponent.as:9051]
    at mx.controls::TextInput/textField_changeHandler()[E:\dev\3.0.x\frameworks\projects\framework\src\mx\controls\TextInput.as:2202]

    private function filterADGHD():void{
    IHierarchicalCollectionView(adghd.dataProvider).filterFunction = browseFilter;
    IHierarchicalCollectionView(adghd.dataProvider).refresh();
    }

  21. I forgot to say that function works fine but when it finishes and return control to IHierarchicalCollectionView(adghd.dataProvider).refresh(); is when error is dispatched.

    Any ideas?

  22. I have a xml:
    var varxml:XML =

    ;

    and then I create a new HierarchicalData object using the previous xml as parameter for constructor:
    adgDataProvider = new HierarchicalData(varxml.node);

    Then I want to filter by @label when typing in a text input control.

    The problem is that when I try
    IHierarchicalCollectionView(adghd.dataProvider).refresh();
    the action script error occurs.

    These are the functions:
    private function filterADGHD():void{
    // assign filter function to the AdvancedDataGrid’s dataProvider
    IHierarchicalCollectionView(adghd.dataProvider).filterFunction = browseFilter;

    // refresh the ADG’s dataProvider
    IHierarchicalCollectionView(adghd.dataProvider).refresh();

    }

    private function browseFilter(item:Object):Boolean {
    var xml:XML = XML(item);

    if(xml.children().length() > 0){
    var hasValidChildren:Boolean = false;
    for each(var node:XML in xml.node){
    if(browseFilter(node)){
    hasValidChildren = true;
    break;
    }
    }
    return hasValidChildren;
    }
    else {
    var string:String = xml.@label;

    return (string.search(searchTextADGHD.text) >= 0);
    }
    }

  23. The xml do not apprears on the previous post, here it is again less than and greater than simbols in html code:

    <node label=”Questionary Categories” type=”root”>
    <node label=”cat1″ type=”category” idQuestionaryCategory=”1″>
    <node label=”New Questionary6″ type=”questionary” idQuestionary=”86″/>
    <node label=”New Questionary5″ type=”questionary” idQuestionary=”77″/>
    <node label=”dfgsdfg” type=”questionary” idQuestionary=”4″/>
    <node label=”New Questionary” type=”questionary” idQuestionary=”51″/>
    <node label=”quest1″ type=”questionary” idQuestionary=”1″/>
    </node>
    <node label=”mt0lg4t0HZ” type=”category” idQuestionaryCategory=”13″/>
    <node label=”jpPq3cSEjK” type=”category” idQuestionaryCategory=”14″>
    <node label=”yUcVD6mJGO” type=”questionary” idQuestionary=”18″/>
    <node label=”New Questionary” type=”questionary” idQuestionary=”19″/>
    </node>
    <node label=”CxR8PFrOr2″ type=”category” idQuestionaryCategory=”15″/>
    <node label=”XP3CUuh05R” type=”category” idQuestionaryCategory=”16″>
    <node label=”New Questionary” type=”questionary” idQuestionary=”21″/>
    <node label=”NvI7nzKguL” type=”questionary” idQuestionary=”20″/>
    </node>
    </node>

    Also, i want to say that when i debug the call to the functions, the line
    IHierarchicalCollectionView(adghd.dataProvider).refresh();

    is called and enters to browseFilter function, i debug it step by step and it loops over all items but when it finishes and return to IHierarchicalCollectionView(adghd.dataProvider).refresh();

    is when error occurs. I hope this description helps to understand what i’m trying to do and when the errors occurs,

    Thanks!

  24. If The Tree is
    A
    A1
    C1
    B
    B1
    C2

    I only want to show node starting with C, without parent Node, how can I achive this?

  25. So, filtering will not help much here. How can the children be shown without their parent?
    However, you can achieve this in two ways –
    1. Create a new XML from the original XML which will contain only the nodes starting with C and use this as the dataProvider for the grid.
    2. Create your own implementation of IHierarchicalData and return the filtered children when getChildren() is called for the root node.

  26. sameer, what is going on with the AdvancedDatagrid in the upcoming Flex 4? is there any new enhancements?

  27. There will be some bug fixes and performance improvements.
    Do you have any particular enhancement/bug fix in your mind?

  28. Sameer, is it possible to completely change the source dynamically at run-time?

    I have an AdvancedDataGrid bound to a flat XML source.

    The contents of myXML changes at run-time.

    In the method where the contents of myXML changes, I refresh the GroupingCollection:
    gcMyGC.refresh();

    Doing this gets me the following error:
    TypeError: Error #1009: Cannot access a property or method of a null object reference.
    at mx.collections::HierarchicalCollectionView/internalRefresh()[C:\work\flex\dmv_automation\projects\datavisualisation\src\mx\collections\HierarchicalCollectionView.as:709]
    at mx.collections::HierarchicalCollectionView/collectionChangeHandler()[C:\work\flex\dmv_automation\projects\datavisualisation\src\mx\collections\HierarchicalCollectionView.as:1068]
    at flash.events::EventDispatcher/dispatchEventFunction()
    at flash.events::EventDispatcher/dispatchEvent()
    at mx.collections::GroupingCollection/refresh()[C:\work\flex\dmv_automation\projects\datavisualisation\src\mx\collections\GroupingCollection.as:449]
    at…

  29. I forgot to mention that since I’m using a flat data source I’m using a GroupingCollection:

    mx:dataProvider
    mx:GroupingCollection id=”gcMyGC” source = “{myXML.myElements}”
    mx:grouping
    mx:Grouping
    mx:GroupingField name=”myGroupingField”/
    /mx:Grouping
    /mx:grouping
    /mx:GroupingCollection
    /mx:dataProvider

  30. Figured it out.

    In case this is useful to someone:

    private function bindGrid():void
    {
    var objGrouping:Grouping = new Grouping();
    objGrouping.fields = [new GroupingField(“name”)];

    var objGroupingCollection:GroupingCollection = new GroupingCollection();
    objGroupingCollection.source = agents.agent;
    objGroupingCollection.grouping = objGrouping;
    objGroupingCollection.refresh();

    adgSales.dataProvider = objGroupingCollection;
    }

  31. Hi, its a great example. I was searching for placing elements in the row where a tree node(group) is formed and your example just solved my problem. I tried using the ArrayCollection through AS and everything was working fine. But when I tried the samething using an XML file as a dataprovider, I am not able to get the exact tree structure. Can you please let me know what should be the XML structure in such a case so that it forms the exact same tree structure as when using ArrayCollection.

      • Good morning Sameer,

        Thanks for your response. The example code behind Comment #23 is where I started. It really gave me a leg up on solving this problem. However, I need all child nodes of a match to be included in my filtered tree.

        The only resolution I could muster was a 2-way search. In addition to the ‘hasValidChildren’ approach in Steven’s post, I also have to recursively evaluate parent nodes until node.parent() returns ‘undefined’.

        For a quick snippet, I pulled my node-matching code into a separate function that the browserFilter() method can use too. That separate function is matchSearch() The parent search looks like this:

        private function hasValidParents(xml:XML, pattern:String):Boolean
        {
          if ( matchSearch(xml, pattern) ) return(true);
          if ( xml.parent() == undefined ) return(false);
          return( hasValidParents(xml.parent(), pattern) );
        }

        and so, if hasValidChildren in browseFilter is false, make the call to hasValidParents().

        WARNING: this approach is *much* more expensive than just searching the children. For the dataset size I’m using, I had to stop triggering the filter on the TextInput ‘change’ event. Instead, I filter on ‘enter’.

  32. My xml file is

    2009-05-04T10:
    27:48+05:30

    2009-05-04T10:27:37+05:30

    COMPLETED

    Operation completed

    2009-05-04T10:27:48+05:30

    2009-05-04T10:27:36+05:30

    I want to show only the children starting with tag….
    subOperation tag

  33. my xml looks like below
    -return userID=”senthil-kumarv@hp.com”
    –subOperation userID=”11″
    —resActions dbid=”7″
    —-endTime 2009-05-04T10:27:48+05:30

    —subOperation userID=”12″
    —subOperation userID=”13″
    –resActions dbid=”6″
    —endTime 2009-05-04T10:27:48+05:30

    And i want to show the children having tag as subOperation

    • Thanks Steven and Marc-André. It’s really good to see you guys in action and helping other people.

  34. Hi Sameer… in my filterfunction, only the parent rows are coming through. It seems like the child rows don’t have the filterfunction applied to them. any ideas?

    I am using a HierarchicalData tag as the source for my ADG. And I’m applying the filter as such…

    IHierarchicalCollectionView(grid.dataProvider).filterFunction = statusFilter;
    IHierarchicalCollectionView(grid.dataProvider).refresh();

  35. Hello Sameer,

    I need a clarification , Actually I need to show the node along with the filtered child which is case insensitive and it should ignore the whitespaces .

    So right now , I am able to show only the filtered node
    like this

    private function myFilterFunc(item:Object):Boolean
    {

    return item.task.match(new RegExp(textSearch.text,”si”));
    }

    where “task” is the datafield for filtering.

    When I tried , filteringv2 by Steven , I am not getting any row.
    (I have just tried , what it was and replaced the datafield alone ,no reg exp) ..Can you help in this regard.

  36. The best way to filter a AdvancedDataGrid is to apply the filter function to the AdvancedDataGrid’s Grouping Collection instance and then do a refresh to that instance thats it.

    a sample snippet how i implemented to a complex hierarchical Data.

    public function filterChanged(filter:String):void
    {
    ADGfilterString=filter;
    // myGColl is my GroupingCollection instance name myGColl.source.filterFunction=myFilterFunc;
    myGColl.refresh();
    ADG.dataProvider=myGColl;
    //(ADG is my AdvancedDataGrid instance name)

    }

    And my Filter function is

    public function myFilterFunc(item:Object):Boolean
    {
    if(ADGfilterString!=”ALL”)
    if(item.name!=ADGfilterString)
    return false;
    return true;
    }

  37. I am passing an ArrayCollection as data provider to the datagrid. When using IHierarchicalCollectionView(datagrid.dataProvider).filterFunction = filterADG; I am getting the item as normal object instead of XML object in my function filterADG. Please help me to resolve this.

    • Can you provide some more information –
      Does your ArrayCollection consists of XML data?
      Are you grouping the data using GroupingCollection?

  38. Hi Sameer,
    Your example is very helpful.But I too have faced the same problem as you filtering only the parent node and child nodes not showing up after filtering.Though the problem is solved by having the dataprovider as XML in my scenario I will be using only ArrayCollection as a dataprovider is there any solution for displaying the child nodes also after filtering the particular parent node.

Leave a reply to Sameer Cancel reply