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!

Tags: ,

35 Responses


  1. [...] 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 Nate’s Code Vault [...]

  2. Bogdan on 24 Oct 2008

    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):

    ?View Code ACTIONSCRIPT
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    if (usingCustomCompareFunction)
    {
        // bug 185872
        // the Sort.internalCompare function knows to use Sort._fields; that same logic
        // needs to be part of calling a custom compareFunction. Of course, a user shouldn't
        // be doing this -- so I wrap calls to compareFunction with _fields as the last parameter
        const fixedCompareFunction:Function =
        function (a:Object, b:Object):int
        {
            // append our fields to the call, since items.sort() won't
            return compareFunction(a, b, _fields);
        };
    .
    .
    .

    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:

    ?View Code ACTIONSCRIPT
    1
    2
    3
    
    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

    ?View Code ACTIONSCRIPT
    1
    2
    3
    4
    
    public function sortNameCompareFunction(o1:Object, o2:Object, fields:Array):int 
    {
        return ObjectUtil.stringCompare(Period(o1).type.name, Period(o2).type.name);
    }

    one with 2 params, the other with 3 params.

    the dataGrid:

    1
    2
    3
    4
    5
    
    <nr:NestedDataGrid id="periodListDG" dataProvider="{periods}">
        <nr:columns>
            <mx:DataGridColumn dataField="type.name" sortCompareFunction="sortNameCompareFunction"/>
        </nr:columns>
    </nr:NestedDataGrid>

    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.

  3. Nate on 25 Oct 2008

    @Bogdan

    In your customSortFunction, try giving the fields argument a default value of null. As in…

    ?View Code ACTIONSCRIPT
    1
    
    public function sortNameCompareFunction(o1:Object, o2:Object, fields:Array = null):int

    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.

  4. Bogdan on 27 Oct 2008

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

    ?View Code ACTIONSCRIPT
    1
    2
    3
    4
    5
    6
    7
    8
    
    if (nestedProperty)
    {
        s = getNestedSort(c);
    }
    else
    {
        s = getDefaultSort(s, c);
    }

    ‘website’ is not a nestedProperty, thus the defaultSort is used, completely ignoring the custom sort function provided.

  5. Nate on 27 Oct 2008

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

  6. Bogdan on 27 Oct 2008

    Add another simple column and use the same function for sorting. It will not work.

    Use this :

    ?View Code ACTIONSCRIPT
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    [Bindable]
    private var dp3:ArrayCollection = new ArrayCollection([
        {name: {firstName: "Nate", lastName: "Ross"}, website: "nross.wordpress.com", website2: "a"},
        {name: {firstName: "Aaron", lastName: "Hardy"}, website: "aaronhardy.com", website2: "b"},
        {name: {firstName: "Josh", lastName: "Buhler"}, website: "blog.joshbuhler.com", website2: "c"}
    ]);
     
    ..
    ..
    ..

    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.

  7. Nate on 27 Oct 2008

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

  8. Bogdan on 29 Oct 2008

    Good work.

  9. Ole Christian on 24 Nov 2008

    Nice!
    I found that it could use a little nullpointerSafe though. So changing getNestedPropertyValue:

    ?View Code ACTIONSCRIPT
    1
    2
    
    object = object[subLevel];
    object = object[level];

    to:

    ?View Code ACTIONSCRIPT
    1
    2
    
    if( object != null ) object = object[subLevel];
    if( object != null ) object = object[level];

    works a little better :-)

    Also the nestedLabelFunction could be a little smoother like this:

    ?View Code ACTIONSCRIPT
    1
    2
    3
    4
    5
    
    var value:* = getNestedPropertyValue(col.dataField, item);
    if( value == null ) return null;
    else if( dateFormatter && (value is Date) ) return dateFormatter.format(value);
    else if( numberFormatter && (value is Number) ) return numberFormatter.format(value);
    else return value.toString();
  10. Nate on 26 Nov 2008

    @Ole
    Thanks for your suggestions. I included the null pointer checks in a new version of NestedDataGrid.

  11. aluminium on 01 Dec 2008

    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.

  12. Nate on 01 Dec 2008

    @aluminium
    I appreciate the catch. I just updated the code with the fix to the problem. If the problem persists, let me know. Thanks!

  13. Cornel Creanga on 07 Jan 2009

    Hi Nate,

    It would be nice if you can handle also lazy loading, which happens often when using LCDS.

  14. Martin on 07 Jan 2009

    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.

  15. Brian on 09 Jan 2009

    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.

    ?

  16. Brian on 09 Jan 2009

    Nevermind… deleted it and created it in source view first. Something wierd with bindings when I tried to create it from design view first.

  17. Ilan Avigdor on 29 Jan 2009

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

  18. Nate on 29 Jan 2009

    I modified the source to make the default SDK the one that is used. Hopefully that will make it easier.

  19. Gareth Arch on 17 Feb 2009

    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!

  20. radek on 22 Feb 2009

    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

  21. radek on 22 Feb 2009

    sorry, but I see that xml is interpret and display example improperly :(

  22. Nate on 22 Feb 2009

    Yes, this is a known bug as I explained in my post.

    Currently this solution does not support XML data providers, but if enough of you ask for it, I wouldn’t be opposed to including that functionality.

    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.

  23. Shreyas on 23 Feb 2009

    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

  24. [...] 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 [...]

  25. Linda Worthington on 29 Jul 2009

    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 value2 to your test variable which was assigned a value of zero. The function should compare value1 and value2. 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

  26. Nate on 30 Jul 2009

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

  27. Rod Nolan on 13 Aug 2009

    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

  28. Nate on 13 Aug 2009

    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

  29. Rod Nolan on 08 Sep 2009

    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

  30. alex on 22 Sep 2009

    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?

  31. Haim on 10 Jun 2010

    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

  32. Nate on 10 Jun 2010

    Nice Haim. I’ll
    add this to the component asap.

    Thanks!
    Nate

  33. Charly on 28 Oct 2010

    Hi,
    have you add the dataTip support ?

    Great component !

    Thanks a lot !

  34. Nate on 28 Oct 2010

    Totally forgot. I’ll see if I can get to it soon.

  35. Charly on 29 Oct 2010

    I’ve some issues with the rendererIsEditor property. Can you check it ?


Leave your comment


ray ban wayfarer ebay ray ban aviators
bag borrow or steal
Buy shoes online india
replica handbags