Home > DataGrid, reusable components > Creating MDataGrid date filter

Creating MDataGrid date filter

In this post I will describe how to create custom filter for MDataGrid (my extension of Flex 3 DataGrid described in one of the previous posts and hosted on http://code.google.com/p/reusable-fx/). I will guide you through creation of date filter as an example.

I consider extensibility one of the most important feature of well designed reusable component and that’s why I am trying to make process of creating new MDataGrid filters as straightforward as possible.

Following “Favor object composition over class inheritance.” rule I decided to split filtering functionality among a number of classes rather than write another thousand lines of DataGrid code. At the first glance the solution may look complicated but after understanding responsibilities of “filters” and “filter editors” it is really straightforward.

Filter editors are the components displayed below column header after clicking filter button. Filter editor know nothing about business logic of filtering, their only responsibility is to present current state of the filter to the user and modify filter according to users actions. Filter editor may be any component implementing IColumnFilterEditor interface. The default implementation of this interface is FilterEditorBase class extending Box which is used as a base class for all standard MDataGrid filter editors. In the presented example we will create filter editor in MXML extending FilterEditorBase class.

Filter classes are where actual filtering takes place, they implement filterFunction which is used to eliminate items from MDataGrid data provider. Filters are also responsible for examining MDataGrid and presenting information about MDataGrid content to filter editors. In our example filter will present minimum and maximum dates found in given column. All filters extends ColumnFilterBase class.

To implement date filtering mechanism two classes have to be implemented: filter class (DateRangeFilter) and filter editor class (DateChooserFilterEditor).

DateRangeFilter

DateRangeFilter extends ColumnFilterBase class and defines four fields: dataMinimum, dataMaximum, minimum and maximum.

dataMinimum and dataMaximum represents a earliest and latest dates found in MDataGrid. These fields are updated by updateOriginalDateRange() function.

minimum and maximum defines the range of dates which will be displayed in MDataGrid. By default minimum is set to dataMinimum and maximum is set to dataMaximum which means that all data are displayed (filter is inactive). It is important to call commitFilterChange() function inside minimum/maximum setter to inform MDataGrid that filter value have changed.

DateRangeFilter also overrides two functions defined in ColumnFilterBase:

isActive getter checks if filter is active i.e. if it may eliminate any items form MDataGrid. In our case isActive getter simply checks if minimum and maximum differs from dataMinimum and dataMaximum.

override public function get isActive():Boolean
{
    return (minimum != dataMinimum || maximum != dataMaximum);
}

filterFunction checks if item passed as an argument should be eliminated. First date displayed in column associated with this filter is extracted from given item and then it is compared against minimum and maximum. If the value is within the range true is returned (item is not eliminated) other ways false is returned (item is filtered out). If no date is fond for the given item true is returned. filterFunction is called many times for every MDataGrid item so it should not be computationally expensive.

override public function filterFunction(obj:Object):Boolean
{
    var value:Date = itemToDate(obj);

    if (value)
    {
        if (minimum && value < minimum)
        {
            return false;
        }
        if (maximum && value > maximum)
        {
            return false;
        }
    }
    return true;
}

You can find two additional functions in DateRangeFilter code: itemToDate() is responsible for extracting date from data item and originalCollectionChandeHandler() refresh dataMinimum and dataMaximum when MDataGrid original collection (copy of data provider) changes.

DateChooserFilterEditor

Good news: creating filter editors in MXML is much simpler than coding filter and the effect is instantly visible!

To make curly brackets binding simpler it is good idea to create strongly typed, bindable reference to filter instance edited by filter editor. The code below is the only code placed in <script> tag of our filter editor:

[Bindable]
protected var filter:DateRangeFilter;

override public function startEdit(column:MDataGridColumn):void
{
    super.startEdit(column);
    if (!column.filter || !column.filter is DateRangeFilter)
    {
        column.filter = new DateRangeFilter(column);
    }
    filter = column.filter as DateRangeFilter;
}

All you have to do now to create cool filter editor is placing MXML components with appropriate bindings and inline event handlers. Below you can see my proposals how date filter editor may look like.

The simple DateChooserFilterEditor consists of two DateChoosers and reset button.
View source is enabled, you can download zipped sources from here.

For better experience open example in separate window.

To save some space I have replaced DataChoosers with DateField and created DateFieldFilterEditor.
View source is enabled, you can download zipped sources from here.

For better experience open example in separate window.

Of course dates may be filtered in many different ways. For example date filter editor may consist of two NumericSteppers to select years. I hope now you can create such filter editor by yourself.

If you have any questions or suggestions don’t hesitate to add a comment!

  1. Nyles
    July 14th, 2009 at 21:02 | #1

    component works great, any ideas how to toggle columns like site below?

    http://www.thomasdecaux.net/flex-components/datagrid-with-dynamic-column-and-checkbox.html

  2. July 14th, 2009 at 22:13 | #2

    Hi,
    I have a ComboBox component with ChecBoxes to change column visibility, I will publish it on this blog when I have a while to clean up the code.

    For now, probably the simplest solution will be to use Repeater with DataGrid columns as dataProvider and CheckBox inside. Something like code below (assuming dataGrid is standard DataGrid or MDataGrid instance):

    <mx:Repeater id="columnsRepeater" dataProvider="{dataGrid.columns}">
        <mx:CheckBox
            label="{columnsRepeater.currentItem.headerText}"
            selected="{columnsRepeater.currentItem.visible}"
            data="{columnsRepeater.currentItem}"
            change="{event.target.data.visible = event.target.selected}" />
    </mx:Repeater>
    
  3. Nyles
    July 17th, 2009 at 00:10 | #3

    Thanks! The Repeater element works well.

    Regarding date function above, i have an external file with elements

    name1

    {new Date(1984,11,04)}

    All elements above the “…” work fine but the last ‘date’ entry does not parse. I tried modifying the entry including removing braces and NEW tag. Can your date filter work on external loaded XML file like above? thanks in advance for your reply, and keep up the good work!

  4. Nyles
    July 17th, 2009 at 00:12 | #4

    my entry got truncated:

    i was asking whether the date object can load from an XML file and i put some pseudocode:

    players
    player
    MB_OriginDate /MB_OriginDate
    /player
    /players

  5. Iwo Banas
    July 17th, 2009 at 01:26 | #5

    If you are using XML data provider (not ArrayCollection declared in MXML) you should use simple MM/DD/YYYY format without braces or new keyword. Unfortunately there is a minor bug in the sources in this post so that it will not work. I will submit the bugfix on http://code.google.com/p/reusable-fx/ tomorrow.

  6. salcu
    September 8th, 2009 at 16:56 | #6

    How can I force to call the filter first time the page is loaded. I need this because if I update one record using filters, after the update is performed the filters are lost(they look like set but not aplied)

  7. Nct
    September 17th, 2009 at 05:04 | #7

    how about filter in advanced datagrid with grouping dataprovider

  8. Iwo Banas
    September 17th, 2009 at 13:53 | #8

    AdvancedDataGrid is the totally different component than DataGrid (they don’t have a common base class) so my filtering solution will not work with AdvancedDataGrid. Moreover the code of ADG is quite dirty so I’m not eager to extend it.

  9. ren
    December 3rd, 2009 at 21:30 | #9

    I want the same filtering functionality on a map,specifically iLOG Elixir Map.

    So here is the scenario,there is filter data, instead of a data grid I have a map that updates based on the filters.

    Can you throw some light how can i do this?

    Your help is greatly appreciated!

  10. Iwo Banas
    December 4th, 2009 at 01:04 | #10

    Hi Ren, I don’t have much experience with iLOG Elixir but if it is using ArrayCollection or any other ICollectionView implementation as a data provider you should have a look at filterFunction property. http://livedocs.adobe.com/flex/3/langref/mx/collections/ICollectionView.html#filterFunction

  11. Deepak
    January 27th, 2010 at 14:08 | #11

    Hi, I too need filtering options for Advanced Data Grid with hierarchical data source, can anyone please help, its urgent need… THANKS A LOT IN ADVANCE

  12. icemaker
    February 1st, 2010 at 17:47 | #12

    Hi,
    great component ! :)

    Is it possible to reset the filter or force the filter (useful if the dataprovider has been updated) ?

  13. Antoni
    February 2nd, 2010 at 15:40 | #13

    Hi Iwo,

    great component, really nice coding but I must say to be disapointed, as there is no solution for Advanced Data Grid… ;) I try to workaround with filterFunction functionality as my dataprovider is an array collection.

  14. Iwo Banas
    February 2nd, 2010 at 23:56 | #14

    Hi icemaker,
    I have this functionality implemented but haven’t committed it yet (I have to clean it up a little).
    I’ll let you know when it will appear in on google code page.

  15. Iwo Banas
    February 3rd, 2010 at 00:10 | #15

    Hi Antoni,
    Unfortunately AdvancedDataGrid is totally independent from DataGrid and it doesn’t seams trivial to port functionality between them.
    Moreover, as mentioned on flexcoders,
    the code quality of AdvancedDataGrid is worse than DataGrid so that it is much harder to extend.

  16. icemaker
    February 19th, 2010 at 14:32 | #16

    Hi again,

    I’ve just noticed that the property headerWordWrap=”true” does not work anymore for colums when using MDataGrid.
    I don’t know if it is a bug or it is intended.
    Do you have any solution to this problem ?

  17. Serhio
    February 28th, 2010 at 00:56 | #17

    Please is there any chance to change format date for sorting to be DD/MM/YYYY ?

    I was looking but i was unable to find anyting….

  18. Iwo Banas
    February 28th, 2010 at 01:22 | #18

    Hi Serhio,

    If you mean format inside filter drop down it is based on DateField and the date format can be changed by modifying formatString property inside filter editor class.

    But probably the better solution would be to use locales with the correct date format defined. The date format is defined inside SharedResources.properties resource bundle.

  19. breeze
    March 22nd, 2010 at 13:41 | #19

    hi!

    first of all: excellent component, nice code, very easy to use… really great!
    i’m missing one thing though… is there any news on the filter reset functionality?

    thanks
    breeze

  20. Iwo Banas
    March 22nd, 2010 at 21:37 | #20

    Hi, filter reset is implemented and committed at http://code.google.com/p/reusable-fx/source/checkout
    I haven’t made binary swc release so you will have to checkout the sources from SVN.
    You can reset all filters by calling resetAllFilters() function on MDataGrid instance.
    Cheers,
    Iwo

  21. breeze
    March 23rd, 2010 at 15:53 | #21

    thank you very very much for the quick response… working great!

    maybe you can help me again with another issue: have you encountered any problems with manipulation of dataprovider (arraycollection) after sorting? after i sort a column, adding a row (addItem on the arraycollection) for example will through some errors (null pointer at ListCollectionView.as:954). without sorting everything works fine… also the global search functionality behaves weird after sorting.

    hints would be greatly appreciated!

    greetz
    breeze

  22. April 13th, 2010 at 23:35 | #22

    What if I do NOT want a filter on one of the columns? How do you make it so the filter icon does not show up on that column?

  23. Iwo Banas
    April 13th, 2010 at 23:39 | #23

    Hi Todd,

    If you don’t want a column to be filterable simply use DataGridColumn (as with standard Flex DataGrid) instead of MDataGridColumn.
    Cheers, Iwo

  24. Kritner Sicarius
    April 15th, 2010 at 22:16 | #24

    This is great component, but I’m pretty new to this aspect of flash/flex programming and I was curious how to do something, if it were even possible.

    Say I wanted to add this grid into a wrapper mxml component along with some additional features like export functionality… basically i’m looking for a method to have a root component such as

    <dgWrapper allMyVariables>

    <MDataGrid usesVariablesFromDgWrapper>

    </MDataGrid>

    <dgTitleBar usesVariablesFromDgWrapper />

    </dgWrapper>

    or something to the effect of having to rewrite as minimal code as possible to implement the component.

    The thing that is throwing me off is since the dataGridColumns are a child of the datagrid, i’m uncertain how to write an mxml component that is basically a box, with a datagrid contained in it, and a applicationControlBar, and allow for the defining of the columns within the datagrid from the application calling it perspective. If this is making no sense at all, i understand completely :P

  25. Iwo Banas
    April 15th, 2010 at 22:32 | #25

    Hi Kritner,
    Sure it makes sense! You can do it in several ways. I think that following one is the simplest.

    Inside <Script> block declare public bindable variable of type Array and bind MDataGrid column property to it.

    <dgWrapper allMyVariables>
    <Script>
    [Bindable]
    public var columns:Array;
    </Script>
    <MDataGrid usesVariablesFromDgWrapper columns="{columns}">
    </MDataGrid>
    <dgTitleBar usesVariablesFromDgWrapper />
    </dgWrapper>
    

    And than, use your dgWrapper like this:

    <dgWrapperComponent>
        <columns>
          <MDataGridColumn properties />
          <MDataGridColumn properties />
        </columns>
    </dgWrapperComponent>
    

    Alternatively you can create MDataGridColumns in ActionScript (inside your component Script block) based on some of its properties but it would probably be more complex solution.

    Hope this is of any help for you.

  26. Kritner Sicarius
    April 16th, 2010 at 14:07 | #26

    @Iwo Banas
    do you have an example of creating the columns through the use of an array? Having trouble finding info on it.

    Would itemRenderers still be possible through setting columns through this method?

  27. Kritner Sicarius
    April 16th, 2010 at 17:48 | #27

    @Kritner Sicarius
    I’ve figured out my issue with the array… still am unsure about how i would accomplish itemRenderers, and I’m also having trouble applying a filterEditor to a column.

    var column:MDataGridColumn = new MDataGridColumn();

    column.filterEditor = IFactory(DateFieldFilterEditor(column));

    not getting any compile errors with that, but the DG is not displaying properly when i have that line in, if i comment it out it seems to work fine (but i don’t have the filterEditor i want)

  28. Iwo Banas
    April 16th, 2010 at 18:06 | #28

    Try the following code: column.filterEditor = new ClassFactory(DateFieldFilterEditor);

  29. Uwe
    May 4th, 2010 at 16:31 | #29

    Hi Iwo! You did a really good job! Thanks for this excellent component!
    I have only one question:
    Is there a way to dynamically set the filter from outside the grid? So that the data is already filtered by defined criteria when the grid is displayed.

    Thanks in advice, Uwe

  30. Henr V. Mempin
    June 7th, 2010 at 15:31 | #30

    Hi Iwo, thanks for you wonderful component, Is there’s a way to use the component filtering function in AIR? by the way i am using the swc version of reusableFX.

    Thank you very much.

  31. June 11th, 2010 at 12:52 | #31

    Hi,
    Great component, I’m new doing custom components.
    I need to add footer to this DG like this example:
    http://www.forestandthetrees.com/openSource/dgFooters/adgFooter.html
    Can you give me an idea in how to implement this? Any documentation in how to create custom components?
    Thansk!

    Johnny

  32. NCT
    July 29th, 2010 at 10:00 | #32

    Hi,
    Thanks for your great control. I used it for many applications, but I have a problem: style of filter button can’t change(filter.png and filter_active.png) when I upgrade my project to sdk 3.5: The color of filter button can’t change from white to red. It will be better if you solve this problem. Thanks!
    NCT

  1. December 6th, 2009 at 07:36 | #1
  2. May 25th, 2010 at 21:26 | #2