Nested Data Grid
If you have ever used the DataGrid before, you may have found yourself frustrated with DataGridColumn and the fact that dataField does not support nested properties (like dataField=”object.property” or dataField=”objectList[2].property).
This is normally not a huge deal because you can either make your own labelFunction and sortFunction to deal with this, or you can just make a custom itemRenderer to display the data you want. However, this take time which makes it not the best solution in general.
There are other people who have solved this problem, but they do it by extending DataGridColumn. In my custom NestedDataGrid I do everything by extending DataGrid without messing with DataGridColumn. I prefer this approach because it is more intuitive for me, and I mean seriously, NestedDataGridColumn is just too long! :)
Another reason to use NestedDataGrid is that I also add support for using brackets instead of just the dot syntax, which allows you to access properties inside of an Array or ArrayCollection, or using properties on an object at runtime (i.e. object[property]). You can also do as many nested levels as you want (i.e. dataField=”object.object1.object2.arrayCollection[0].property1…). It also works great for XML-based data providers as of my latest update.
See the demo and get the source here (Right click to View Source).
Also, feel free to combine NestedDataGrid with my ScalableDataGrid custom component. Just download both components and then make NestedDataGrid extend ScalableDataGrid or vice versa!
[...] Custom Nested DataGrid I recently made a solution to this problem by extending DataGrid and setting up a default labelFunction and sortCompareFunction that traverse nested object properties. It supports traditional nested object properties like dataField="object1.object2.object3.property", and it even supports nested object properties like dataField="object1.objectList1[2].object3.objectList2[1].property". Plus it supports the sorting of Date objects. Feel free to check it out at Nested Data Grid Nates Code Vault [...]
Hello,
I’ve used your nestedDataGrid and found one problem.
When you use a custom compare function for the dataGridColumn, there is a mix in the function calls within the flex api, namely in Sort.as. I use flex 3.1.0 sdk.
In Sort.as there is this piece of code (line 675):
which, in case of a custom compare method will need a function with 3 params (o1, o2 and fields). If I use a compare function with 3 params I get an error in Sort.as at line 526 at this piece of code:
direction = fieldsForCompare ? compareForFind(values, obj, fieldsForCompare) : compareForFind(values, obj);as fieldsForCompare is null, compareForFind with 2 params is called, thus the error. The custom sort compare function cannot be used either with 2 nor 3 parameters.
It would be of great help if you could provide an example where you use a custom sort compare function within your nestedDataGrid.
some snippets of code that I used:
the custom sort functions
one with 2 params, the other with 3 params.
the dataGrid:
The Period object has a field named ‘type’ that is an Enum.
The above example works without the custom sort function but I’ve used it to show the problem.
@Bogdan
In your customSortFunction, try giving the fields argument a default value of null. As in…
I found that this worked great for me (Also using SDK 3.1).
If you don’t set the default value of null, you get all sorts of errors. One downside that I didn’t really consider is the fact that the fields array is null, so if you use the same SortCompareFunction across multiple DataGridColumns it would be difficult to tell which column you should perform the sort logic on. To address this issue, I made the sortColumn:DataGridColumn public instead of private so you can reference it in your SortCompareFunctions. I also included an example of how to do this in the download.
The sort is ok on the nested columns (a.b type) but is not working on the unnested ones that have a custom sort function (on the ‘website’ column from your example).
‘website’ is not a nestedProperty, thus the defaultSort is used, completely ignoring the custom sort function provided.
@Bogdan
I see what you mean now. I modified the getDefaultSort method to take custom sort functions into account where it was not doing so before. NestedDataGrid.as is now updated to reflect this. I appreciate the feedback.
Add another simple column and use the same function for sorting. It will not work.
Use this :
When you press on ‘Website’, the sort is made ok (at line 139 var s:Sort will be null and it will get initialized further into the code). Now press on ‘Website2′. At line 139 var s:Sort has in fields the previous field (’website’) which is wrong and the sort is broken.
@Bogdan
Good catch. Fixed the bug in the getDefaultSort function where a SortField is reused if it exists which cause the sort to execute on the SortField object instead of the Sort. Thanks.
Good work.
Nice!
I found that it could use a little nullpointerSafe though. So changing getNestedPropertyValue:
to:
works a little better
Also the nestedLabelFunction could be a little smoother like this:
@Ole
Thanks for your suggestions. I included the null pointer checks in a new version of NestedDataGrid.
Found another issue with sorting – have 4 columns, one is from a nested object, the others are not. Sorting on the non-nested columns breaks when you switch between non-nested columns. The Nested column serves as a reset button to fix sorting on the other columns.
If column C is the nested one, I can sort column A just fine. Then i try sorting B and it breaks. Then I can no longer sort column A,B, or D until i sort by column C (the nested one) again.
@aluminium
I appreciate the catch. I just updated the code with the fix to the problem. If the problem persists, let me know. Thanks!
Hi Nate,
It would be nice if you can handle also lazy loading, which happens often when using LCDS.
Brilliant … can’t imagine why Adobe didn’t have this from the outset! Thanks, you saved me a lot of time as I was about to do the same thing, and then thought, surely someone else has this problem, especially using 3rd party Web Services or RSS feeds.
Doesn’t seem to like existing within a Canvas container? You sample app only complains about an unknown SDK: “Flex 3.1″ but it runs. However, when I stick the same code into my Canvas is no longer seen and gives me an error.
?
Nevermind… deleted it and created it in source view first. Something wierd with bindings when I tried to create it from design view first.
@Brian
You just need to modify the SDK used in the sample code project to an existing one (right click Project->Properties->Flex compiler->Flex SDK version) installed on your machine.
choosing the default (3.2) worked for me.
I modified the source to make the default SDK the one that is used. Hopefully that will make it easier.
I love this…I had just begun refactoring my code today and was about to create a class for all of the many sort/display functions that I was going to have to cram together. Then I come across this class and find that it should solve all of those issues and will slim my code down nicely
Brilliant!
bug.
sort not working when dataProvider is xml
fix: correct getNestedPropertyValue() ?
sample ex:
Alaric
Cole
alaric@oreilly.com
4155558273
mobile
akdfasd
94001
07/20/1979
0×00FF00
false
Blaric
ACole
alaric@oreilly.com
258273
mobile
Green St
194001
07/20/1980
0×00FF00
false
sorry, but I see that xml is interpret and display example improperly
Yes, this is a known bug as I explained in my post.
If anyone has already implemented NestedDataGrid with XML-based data providers, feel free to email me (nross83 at gmail dot com) and I will include the addition in my source code.
I have been pretty busy recently, but I will be graduating in April and I hope to include XML data provider support at that time.
Hi,
First of all, Thanks a lot for the excellent component.
I see that data binding is not working. If i change data in my value objects in the collection, then they dont get updated in the UI. Am i missing some thing?
Thanks,
Shreyas
[...] A few months ago, I created a component called NestedDataGrid, to address the annoying issue with DataGrids not being able to support nested properties within objects. This component allows a DataGridColumn’s dataField property to contain a property chain such as: “object.property.propertyList[2]“. This is something that wasn’t possible with a default DataGrid. However, being busy with school (just got my master’s degree in April! wahoo!) and work, I didn’t have time to build in support for XML-based dataProviders. After a few requests, I decided to change that and update the NestedDataGrid to support XML. NestedDataGrid — able to handle complex dataFields [...]
Nate -
This is a fantastic extension of the DataGrid. Exactly what I needed. I did find one small problem with your custom nestedPropertySortFunction. For sorting Collections, the ObjectUtil.compare function was returning -1 every time. You were comparing
value2to yourtestvariable which was assigned a value of zero. The function should comparevalue1andvalue2. I made the following code change and it works perfect for all data types now:var value:int = ObjectUtil.compare(Object(value1), Object(value2));
Hope this helps. Thanks for the great contribution.
LW
@Linda,
Thanks for the catch. Must not have been thinking straight when I made my “XML data provider support” update a couple weeks ago
. Glad to hear it helps.
Nate,
Thanks for doing this! No more code required to wrestle dataProviders flat!
I’m currently trying to re-purpose your work for the Advanced Data Grid and I’m finding that it’s not as straight forward as I’d like. Basically, I need the dataField=”obj.obj.list[x]” feature but I also need the column grouping feature that only the AdvancedDataGrid provides. So I’m stuck using the AdvancedDataGrid which precludes me from using your NestedDataGrid. Does anyone know of an extension that does this? I don’t suppose there’s a NestedAdvancedDataGrid in the works?
Thanks,
Rod
Hi Rod,
There is not a NestedAdvancedDataGrid in the works currently
. However, I will try looking at it this weekend and see if I can’t get one working for you.
- Nate
Just dropping in with a quick update… I ended up ditching the AdvancedDataGrid in my project in favor of your NestedDataGrid.
While the point of the NestedDataGrid component is to avoid the need to write custom renderers and sort functions, all of the columns in my project that could have used the nested dataField HAD to use itemRenderers and itemEditors. Specifying a complex nested value (”obj[x].property.etc.etc”) for the dataField property in these cases introduced sorting problems.
To solve the sorting issue, I ended up using a mixture of DataGridColumn and DataGridColumnNested instances (an extension of DataGridColumn that includes a single property called nestedSortField).
For the columns whose dataField was a simple value, I used regular DataGridColumn instances and fed them with the simple dataField value. The default sort functionality worked fine here.
For the columns that could have taken advantage of the nested value for their dataField, I had to use itemRenderers and itemEditors anyway. And using a nested value for dataField property introduced sorting problems for those columns.
In those cases, I used DataGridColumnNested instances instead. I fed the nested value into the nestedSortField property instead of the dataField property. After I modified the logic in the NestedDataGrid’s sortByColumn function to evaluate the column’s type before sorting, everything came together.
The end result was that I was able to use custom itemRenderers and itemEditors for the complex columns with no dataField specified. Sorting was enabled for those columns by the combination of the nestedSortField property and a smarter sortByColumn function.
It took a while to come around to this solution but it works like a charm in my situation and your component made it possible. So “Thanks!” again.
I’m not sure how to post code here but if anyone wants this stuff, let me know. I’m more than happy to share.
Rod
i’ve been having problems with the following dataset:
{
“race”:
[
{
"lap":
[
{
"Runner":"Kerry",
"Time":128
},
{
"Runner":"Mary",
"Time":116
},
{
"Runner":"Gerry",
"Time":159
}
]
},
{
“lap”:
[
{
"Runner":"Kerry",
"Time":128
},
{
"Runner":"Mary",
"Time":116
},
{
"Runner":"Gerry",
"Time":159
}
]
}
]
}
i’ve been trying to use this as my function:
import mx.rpc.events.ResultEvent;
import com.adobe.serialization.json.JSON;
import mx.controls.Alert;
import mx.collections.ArrayCollection;
private function onJSONResult(event:ResultEvent):void
{
//get the raw JSON data and cast to String
var rawData:String = String(event.result);
var arr:Array = (JSON.decode(rawData) as Array);
var dp:ArrayCollection = new ArrayCollection(arr);
//pass the ArrayCollection to the DataGrid as its dataProvider.
dg.dataProvider = dp;
}
and i know it successfully retrieves data – i’ve used toString() to show that the data is retrieved.
I think the stage I’m confused at is working out the nesting for the datagrid:
From my JSON (which i’m fairly sure is valid [i used JSONView, the Firefox extension to validate it]) can you determine the nesting I should use?
Thanks Nate for this post; it certainly helped me.
Now – something in return… I’ve needed to shop datatips for nested properties (e.g. long description for ‘name’ column of some object). I’ve extended your code as follows:
1. assign the function for data tips
override public function set columns(value:Array):void
{
for each (var c:DataGridColumn in value)
{
if (isNestedProperty(c.dataField))
{
…
}
/// Haim: support nested properties for data tips
if (isNestedProperty(c.dataTipField))
{
if (c.dataTipFunction == null)
{
c.dataTipFunction = nestedDataTipFunction;
}
}
}
….
}
2. implement the function
protected function nestedDataTipFunction(item:*):String
{
var value:* = getNestedPropertyValue(this.sortColumn.dataTipField, item);
if (value is Date)
{
if (dateFormatter != null)
{
return dateFormatter.format(value as Date);
}
else
{
return (value as Date).toDateString();
}
}
else
{
if (value != undefined && value != null)
{
return String(value);
}
}
return “”;
}
Feel free to embed it for the good of others.
Haim
Nice Haim. I’ll
add this to the component asap.
Thanks!
Nate