Monday, February 11, 2013

Flex GridColumn, complex objects, and default sorting

I recently discovered something about the spark GridColumn. You can pass in a complex type for the dataField.

Say for example that your dataProvider is a collection of Users:

public class User
{
    public var name:String;

    public var id:String;

    public var manager:User;

}


You can specify the gridColumn.datafield as "manager.name"
<s:GridColumn dataField="manager.name" />

This is really cool. Can you can see it in the source code down where the datafield property is set. There is an internal dataFieldPath variable.

Here is the problem that I ran into. I had several columns that had these nested values and I wanted to set up each column to have case insensitive sorting, which is not the default. Using a shared sortFunction was difficult, because I also didn't have direct acces to the dataFieldPath (the nested property), and it would have too much work (in my opinion) to build the object within the sort and then grab the string and then lowercase and compare. It was especially too much work, because it would have negated any label functions that were already in place for these columns.

So here is my solution:


package com.workbook.core.view.datagrid
{
    import mx.formatters.IFormatter;

    import spark.collections.SortField;
    import spark.components.gridClasses.GridColumn;
    import spark.components.gridClasses.GridSortField;

    public class CaseInsensitiveComplexDataPathGridColumn extends GridColumn
    {

        public function CaseInsensitiveComplexDataPathGridColumn()
        {
            super()
        }



        override public function get sortField():SortField
        {
            var sf:SortField = super.sortField;
            const isComplexDataField:Boolean = sf is GridSortField;
            var cF:Function = null;
            if (isComplexDataField && sortCompareFunction == null)
            {
                sf.compareFunction = dataFieldPathSortCompareCaseInsensitive;
            }
            return sf;
        }

        protected function dataFieldPathSortCompareCaseInsensitive(obj1:Object,
                                                                 obj2:Object):int
       {
            if (!obj1 && !obj2)
                return 0;

            if (!obj1)
                return 1;

            if (!obj2)
                return -1;

            const obj1String:String = itemToLabel(obj1).toLowerCase();
            const obj2String:String = itemToLabel(obj2).toLowerCase();

            if ( obj1String < obj2String )
                return -1;

            if ( obj1String > obj2String )
                return 1;

            return 0;
        }


    }

}

What this does, is that when you click on the column header, it grabs the sortfield from the gridColumn (that was the easiest public property to get at). Fortunately if the dataField is complex, it builds a GridSortField object instead of a SortField.

I check for the GridSortField and if there is not already a sortCompareFunction, I assign it to my case insensitive sort function.

Then within the sort function, I call itemToLabel, which fortunately does all of the heavy lifting to determine the nested object's label, calling any labelFunctions as needed.

Then I force to lowercase, and done.

To use, I replace my GridColumn in my dataGrid definition, with CaseInsensitiveComplexDataPathGridColumn.