DataGrid with client-side filtering and searching

I am working on reusable Flex 3 DataGrid extension allowing client-side filtering and searching, and probably many other features in the future. Although it is not finished yet I decided to publish the result of my work to get some feedback from you:-)

The project source repository is located at http://code.google.com/p/reusable-fx/
You can also view source of the example below.

I consider few things extremely important when creating new components:

Reusability

New DataGrid component should work not only for one cool demo but could be reused and reconfigured in many different applications. Every newly added element should support styling and/or skinning.

Extensibility

New DataGrid should allow further extension without need to rewrite too much code. During implementation of new features standard, well documented interfaces and patterns should be created to make further development easier. For example current version support filtering of text and numerical data but creation of filter for date ranges or images should be straightforward.

Compatibility

Compatibility with standard Flex DataGrid should be regarded very important and every incompatibilities should be well documented. For example one should be able to use filtering on one column when leaving other columns unchanged. Newly created interfaces should be similar to those known from standard Flex components.

I am trying to follow the rules above but of course it is not always possible. Especially if you are working on something for a long time it is difficult to tell what is intuitive for “ordinary user”. So if you play with example below and found something which in your opinion should work differently don’t hesitate to add a comment or create new issue on http://code.google.com/p/reusable-fx/issues/list !

Summary

MDataGrid can be used just like standard DataGrid: you define a dataProvider and columns either in MXML or in ActionScript. If you want a column to allow filtering replace DataGridColumn with MDataGridColumn which by default has a text wildcard filtering enabled. If you prefer to filter column in a different way change filterEditor property of the column to point to some filter editor from com.iwobanas.controls.dataGridClasses.filterEditors package. You can find more information in the ASDoc in the source code.

Example

This example demonstrates live searching and filtering MDataGrid with about 400 rows.
View source is enabled, you can download zipped sources from here.

For better experience open example in separate window.

Share on Google+Share on FacebookShare on LinkedInPin on PinterestTweet about this on Twitter

140 thoughts on “DataGrid with client-side filtering and searching”

  1. Are you able to start with a default filter? For example, with the MultipleChoiceFilterEditor, have some checked and some unchecked to start when the grid loads? Thanks again!

  2. I am working on a generic/extendible way of serializing and deserializing filter values but it is not ready yet.

    For now you can initialize filter in ActionScript, for example inside creationComplete event handler. The code below shows how to initialize MDataGrid column (with id=”column” and MultipleChoiceFilterEditor set) with values “label1” and “label2”:

    column.filter = new MultipleChoiceFilter(column);
    MultipleChoiceFilter(column.filter).deselectAll();
    MultipleChoiceFilter(column.filter).selectLabel('label1');
    MultipleChoiceFilter(column.filter).selectLabel('label2');
  3. Unfortunately this doesn’t appear to be working for me, as the MultipleChoiceEditor renderer doesn’t get created initially until you click the filter button in the column once. Then if I execute this code, it works as expected.

  4. This is most definitely a hack, but since the filterEditor can’t be set until an instance is created, in DropDownFilterHeaderRenderer.as I’ve added an init function:

    protected function initFilterDropDown():void
    {
    if( column && column.filterEditor)
    {
    var editor : IColumnFilterEditor = column.filterEditor.newInstance() as IColumnFilterEditor;
    editor.column = column;

    column.filterEditorInstance.closeEditor();
    }
    }

    I invoke this function from the createFilterButton function. Then in the creationComplete event handler, execute the following code:

    var filter : MultipleChoiceFilter = myCol.filter as MultipleChoiceFilter;
    filter.deselectAll();
    filter.selectLabel( “BIOS” );

    What performance implications this will have I do not know. I’ll look forward to your official enhancement for this functionality. Thanks again!

  5. Having an issue getting headerWordWrap to work. Any Ideas on where I should look to get it fixed?

    Any help is greatly apprciated….

    Thanks

  6. Realy great component. But i have a question about using it in a localized application. Normally if a column is resized and the text is longer as the width of the column “…” will be displayed after the Header and also after the label in the list of the multiple choice filter.
    In the localized environment “null” will be displayed behind the label. It seems any string could not be localized. I searched for a while, but i can’t find out why the null ist displayed behind the label.

    Any ideas?

  7. Hi,

    Header renderer is using standard Label component so if Label works for you this component should work as well. Label is using “truncationIndicator” property from “core” resource bundle which is by default set to “…”. Make sure you are not overriding sdk “core” resource bundle by your own resource bundle with the same name.

    Unfortunately I haven’t got time to localize MDataGrid yet so tooltips may be displayed in English regardless of locales set. You can try to localize MDataGrid by yourself and submit the patch to http://code.google.com/p/reusable-fx/ 🙂 There are only few hardcoded string so moving them to resource bundle shouldn’t take too much time (I really don’t have that time to do this right now).

    Cheers,
    Iwo Banaś

  8. Unfortunately headerWordWrap is not supported yet. Have a look at com.iwobanas.controls.dataGridClasses.DropDownFilterHeaderRenderer class. It is currently using Label component to display header. Probably Label should be replaced with UITextField or Text.

    It you will manage to add headerWordWrap support don’t hesitate to submit your changes to http://code.google.com/p/reusable-fx/ .

    Cheers,
    Iwo Banaś

  9. This is a fantastic datagrid component. Thank you very much for the hard work and sharing of your efforts.

    There are a few things I’d like to propose and if I missed it from the above posts I apologize.

    1. The ability to retain the datagrid filter for the Multiple Choice Filter Editor after the dataset has been refreshed.

    2. The ability for the datagrid to retain it’s sort after the dataset has been refreshed.

    3. The ability for the datagrid to retain the selected row after the data has been refreshed.

    4. The ability to have the filters slide to the left when the columns are to the right side. They can get lost from view.

    I think these four items would be a great addition to your already stellar component.

    Cheers,

    Phil

    PS – I’m not that good of a developer to make this additions myself – yet. 🙂

  10. One other thing – I’ve downloaded your source and for some reason the filter icons are not changing styles when I filter a column as they do in your online demo. I haven’t changed a thing.

    Thanks again!

  11. Hi Phil,
    Thanks for your feedback!
    I have no idea why the filter icon is not changing for you, I will have a look at it next week. Support for data provider modification (retaining filter selection) is on my TODO list, but your post may give it a higher priority 🙂

    If you have any other new features or bugs add new issues (one per feature/bug) at http://code.google.com/p/reusable-fx/issues/list please. Feature requests listed inside comments can easily be lost.

    Cheers,
    Iwo

  12. Great component; thanks for source !

    I’m trying to create via AS3, MDataGridColumns to add to a MDataGrid (myDataGrid) and all seams to work fine.
    But I’m not able to change the filterEditors of the new MDataGridColumns just created. I’ve followed the instruction in “Summary” but I’ve some “cast error”.

    below my function:

    private function addDataGridColumn(index:int, colName:String, colType:String):void {
    var dgc:MDataGridColumn = new MDataGridColumn(colName);
    dgc.headerText = getHeaderInfo(colName);
    dgc.dataField = colName;
    if(colType==”INTEGER”) {
    // The error appear on the following line
    dgc.filterEditor = new NumberRangeFilter(dgc); }
    var cols:Array = myDataGrid.columns;
    if(index==0) cols.pop();
    cols.push(dgc);
    myDataGrid.columns = cols;
    }

    Thanks in advance.

    Roberto

  13. Hi Roberto,
    filterEditor field is IFactory (just like itemRenderer etc.), to change its value from AS3 you may use ClassFactory.

    dgc.filterEditor = new ClassFactory(NumberRangeFilter)

  14. In the search field above can it be used to give a filtered datagrid instead of highlighting the matched records?

  15. Hi, one small issue we’re having, is that since we’ve implemented the filtering, the data in the datagrid does not show up initially.
    If we click anywhere on the scroll bars or in the header columns, then the grid gets populated, otherwise it’s blank.

    Mark.

  16. I’ll try and get some code posted, but it seems related to Datapaging.
    Unfortunately I don’t fully understand how our paging is working, since the code we are using was automatically generated by WebOrb.
    But as soon as we implement the MDataGrid, the paging ability of the Datagrid ceases.
    Mark.

  17. Just to clarify. Our MDatagrid is bound to a Dataprovider that is an ArrayCollection filled in by a call to a WebOrb function which retrieves records from a MySql database. I don’t understand the relationship between the provider and the display of data on the screen. If the provider returns 100 records and the datagrid has space to show 20, when the user scrolls, does the datagrid make another call to the dataProvider to get more records? Or are these records waiting in some memory ready for display? Our WebOrb ‘findall’ DB function has some pagesize number that we can set, presumably to limit the records read from MySQL. But I don’t understand when the grid goes back to the provider for more data. In our case, we have to click on the scroll bar (or sort columns) to force it to get more data and display it.
    Mark

  18. I experimented using an ArrayCollection as a dataProvider and the MDataGrid works fine. But as soon as I use WebOrb’s findAll()
    (http://www.themidnightcoders.com/fileadmin/docs/flexclient/weborb/data/DataMapper.html) then the grid must first be horizontally scrolled or one of the columns clicked before it will fill in data. Whatever event causes the grid to be filled in initialy, is not getting fired when you use MDataGrid with WebOrb’s DataMapper class for client-server record retrieval.

  19. I’m trying to save the grid layout by creating a SharedObject to save the information locally. That way the user can arrange the grid as they like and have it persist for all sessions. I’ve got it working except for the ability to save the selected filter type for each column. In the MXML you can set the filterEditor with a string for each MDataGridColumn but I can’t dynamically re-assign that filterEditor type when reloading from the SharedObject. Any ideas how this can be done? Still learning so thanks for the patience.

  20. Hi Iwo,

    How much would you charge to make the datagrid filter persistent when the dataset is refreshed? My customers love the filters but are annoyed when the dataset gets refreshed and the filter is not reapplied.

    Thanks for the help,

    Phil

  21. This is a really cool grid, first of all thanks for sharing!
    But, how can I get all selected filters? I’ve tried it with the selectedLabels, but this gaves me all filter labels, if selected or not. I try to save only the selected ones…

  22. @John
    Every filter has “isActive” property determining if it is active (the filter button is red then). In case of MultipleChoiceFilter it is considered inactive when all labels are selected. In this case selectedLabels contains all labels. If you deselect some labels selectedLabels property should not contain them.
    Hope this answers your question.

  23. Hello Iwo,

    I have a problem with the datagrid not reflecting the actual values immediately. It appears to require user manipulation AFTER the creation is complete. Perhaps some of the datagrid is populated asynchronously and thus a secondary action (or pause) is needed in order to reflect the true values, is this the case?

    I have still not solved the issue, but am wondering if this is a good route to investigate (I have dealt with RPCs before in a similar manner but didn’t want to fret over this if you have seen this issue and have a solution that is unrelated to my suspicion).

    Thanks,
    John

  24. As a little note – if I change the entire structure back to a conventional DataGrid, it displays correctly (i.e. without having to have any action on the grid like with the MDataGrid).

    Any help is greatly appreciated.

  25. Hi Iwo,
    Your filtering component is absolutely wonderful. But I need the search box to work in a little bit different manner. I just want the searched items to be displayed rather than just highlighting them. How to do that? Can you help me with that?

  26. I have used this component…It works well thanks…But after upgrading to the newer source (to do date range selections) I have lost my CSS style…The drop down is now transparent…Here is my CSS…Do you know what I should change?

    DropDownFilterEditorBase{
    border-style: solid;
    background-alpha: .5;
    background-color: grey;
    drop-shadow-enabled: true;
    padding-bottom: 5;
    padding-left: 5;
    padding-right: 5;
    padding-top: 5;
    }

    DropDownFilterHeaderRenderer{
    padding-bottom: 0;
    padding-left: 5;
    padding-right: 0;
    padding-top: 0;
    horizontal-gap: 0;
    vertical-align: middle;
    filter-button-style-name: dataGridFilterButton;
    filter-button-active-style-name: dataGridFilterButtonActive;
    }
    .dataGridFilterButton{
    padding-bottom: 4;
    padding-left: 4;
    padding-right: 4;
    padding-top: 4;
    icon: Embed(“/assets/style/assets/icons/filter.png”);
    }

    .dataGridFilterButtonActive{
    padding-bottom: 4;
    padding-left: 4;
    padding-right: 4;
    padding-top: 4;
    icon: Embed(“/assets/style/assets/icon/filter_active.png”);
    }

  27. Hi Bill,
    all you have to do is to change the first CSS declaration from DropDownFilterEditorBase to DropDownFilterEditor. I’ve changed filtereditors architecture a little (replaced inheritance with composition) and renamed classes to reflect this change. Sorry for confusion.
    Cheers,
    Iwo

  28. Is there a way to add a currency symbol to a column that is filtered with a sliding filter? I have tried using a label function but all I end up in the column is $0.00.

    Thanks

  29. Hi TomT, this is a typical scenario when labelFunction should be used. You can also use formatter to make your code more flexible.

    First declare CurrencyFormatter with properties that suits you and than your labelFunction may look like this:

    labelFunction(item:Object, column:DataGridColumn):String
    {
        return formatter.format(object[column.dataField]);
    }
    
  30. Hi Iwo,

    I’m new to flex and it’s hard for me to understand how things work for it. For the filtering and search, is it possible to add a replace function to replace the item that has been found through search. like a find/replace functionality in a page.

    Thanks and warm regards

  31. Hi Athrin,

    It is possible to implement find/replace function in Flex but it definitely won’t be easy. In case of DataGrid you would have to modify dataProvider items properties manually (probably using square brackets notation i.e. item[column.dataField) and if your dataProvider items contain fields of different type than String it would be even harder. So if you are new to Flex I’m afraid you will have to go without replace function 🙁

    Cheers

  32. I have used this component in the past…It works well. I am using again in a slightly different way…

    In this situation I am building columns dynamically. But the widths a having an issue…After the first load (columns and data). All is well but if another column and data request is made datagrid behaves in a strange manner. The columns expand to the correct size but after the data is loaded the header widths get very small but the data widths remain the correct size

    Here is some code…

    var colResult : ArrayCollection = event.result.Columns.Column;
    var colArray:Array = new Array();
    var oColumnDef:MDataGridColumn;

    for (var i:int=0;i<colResult.length;i++) {
    oColumnDef = new MDataGridColumn(colResult.getItemAt(i).ID);
    oColumnDef.dataField = colResult.getItemAt(i).Label;
    oColumnDef.headerText = colResult.getItemAt(i).ID;
    oColumnDef.width = colResult.getItemAt(i).Width;
    oColumnDef.headerText = colResult.getItemAt(i).ID;
    //oColumnDef.filterEditor = new ClassFactory(mcFilter);
    //oColumnDef.filterEditor = new (com.iwobanas.controls.dataGridClasses.filterEditors.MultipleChoiceFilterEditor);
    oColumnDef.sortable = true;
    oColumnDef.visible = true;
    oColumnDef.editable = false;
    oColumnDef.wordWrap = false;

    colArray.push(oColumnDef);
    }
    modelLocator.colArray = new Array();
    modelLocator.colArray = colArray;

    PS How do i call the filter functions from an actionscript class?

  33. i love the datagrid however I am having some issues in referencing
    for example:

    Any ideas how I can reference a column when a user clicks on a row?

    The main reason I am creating a reference is because on of my columns is defined as a description which has a lot of text and doesnt show. I tried word wrap and it made it worse. Is there a way to have just that individual cell autoscroll?

  34. This is fantastic. I have a minor issue. The sorting is by default non-numeric. Can you tell me how to instruct certain columns to sort numerically?

  35. @Iwo Banas
    Thanks Iwo. This issue arose since I was previously setting sorting behavior to the dataProvider. It took me a couple minutes to figure out how to set it to the DataGridColumn instead and finally did in the following manner:

    public function sortNumeric(field:String):Function
    {
    return function(obj1:Object, obj2:Object):int
    {
    return ObjectUtil.numericCompare(obj1[field],obj2[field]);
    }
    }

    used in the MXML as sortCompareFunction=”sortNumeric(‘bookedRevenueCol’)”

    Thanks again.

  36. Hello,

    I binded the MDatagrid component to an arraycollection which I want to update. For example I want to delete a row in the datagrid on a button click, so I delete the selectedItem in the arraycollection. However the datagrid is not updated. To do so I need to click on the header of the datagrid to see the row being removed. Could you tell me how to fix that ?
    Thanks !

  37. Hi Iwo, This component is excellent. I have just started working on Flex. And we started using Flex4. I imported the whole source code and pointed it as library to my source project. But when I run the sample test, it doesn’t display filter icons, But on mouse over I can see some Square. And on click of it, it is giving the error #1009 and says null proprty or method.
    Could it be because of, Flex 4?
    If so, How can make this component working on Flex 4.

    Thanks for sharing this gr8 component.

  38. Hi Jay,
    I haven’t tested this component with Flex 4 yet. But it should be easy to port it to Flex 4 since both Flex 3 and 4 are using the same DataGrid component.
    I’ll have look at this later this week. For now make sure that you are using the latest MDataGrid from http://code.google.com/p/reusable-fx/
    Cheers, Iwo

Leave a Reply

Your email address will not be published. Required fields are marked *