Rendezvous with technology

Filtering of Hierarchical Data in AdvancedDataGrid

Posted by: Sameer on: October 12, 2007

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

60 Responses to "Filtering of Hierarchical Data in AdvancedDataGrid"

Hey cool ! I didn’t now you finally started a blog .. Cheers !!
Nice post, Thanks

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

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.

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

Thanks

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

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.

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

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?

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!

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.

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!

Sameer, can you post an example of filtering all nodes in the hierarchy?

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?

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…

If the value is coming as Strings, the filter should work fine.
Are you applying any formatter to the value?

Sameer, I’m still having issues filtering the advgrid. Can you take a look?

http://s256908546.onlinehome.us/advgrid/index.html

Thanks

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.

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.

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;
}

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.

Thanks Sean for posting this. Many people must be waiting for this sample to come up.

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

Hey guys,

I’ver merged Sean’s and Sameer’s code to filter children.
You can get the sample file at this link: http://computerarts.ca/_files/_flex/filtering.zip

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…

Can you log this issue in the bug base

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

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();
}

Can you send a simple sample of what you are trying to do?

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?

Actually, the filter and sort is applied only when refresh() is called.

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);
}
}

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!

I created a sample with the data you provided and things seems to work in it.
Can you send me your mail id so that I can send you the sample I created.
My mail id is prosameer@gmail.com

My mail is ddominguez@gcpglobal.com

I have tried the example in

23. Steven – February 11, 2009

Hey guys,

I’ver merged Sean’s and Sameer’s code to filter children.
You can get the sample file at this link: http://computerarts.ca/_files/_flex/filtering.zip

and it works fine so i don’t know why my test do not works

i appreciate you send the example, i will try it and i hope it helps me to solve my problem.

How to do dynamic tree inside advanced datagrid.Please send the code

Please be specific. What exactly do you want to do?

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?

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.

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

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

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…

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

Can you provide a simple sample of what you are doing?

Hi Sameer.

I made a simple sample and sent it to you.

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;
}

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.

You can try the sample Steven has posted in Comment #23 above.

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’.

Check my new post further down.

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

sorry not able to paste my xml file dont know hoe to format it

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

Hello everyone!

I updated my example in order to allow a filter on parent and children.
You can download the latest files here: http://computerarts.ca/_files/_flex/filtering_v2.zip

Thanks to my friend Marc-André for finding a solution!

Cheers
Steven

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

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();

I’ve figured it out.. thanks anyways..

I am building a basic ADG with a renderer showing the children.
A good example (not mine) is http://www.objectsatellite.com/html/FlexSolutions/ADGWithContainedADG/DataGridWithChildDataGrid.html

However when I apply a filter to the parent data (e.g name) then the children under the filtered rows do not show.

Any Ideas?

Please have a look at comments #23 and #50.
Steven has posted a sample for this.

Leave a Reply