Features wanted!
To make Flex datagrid completed, I would like to have the following featues. AutoCompleted Search – Locate the data I want quickly if there are too many rows in my grid. Internationalization – Handle currency, number and date format. Data Export – Output the data in csv format, so users can import to Excel. Pagination - If I give the total number of records, the subset of the data rows and the number of rows per page, the grid should be able to do pagination and fire the events when user clicks on other pages. This article I will show you how to make these happen.
![]()
AutoCompleted Search
After you obtain the resultset from the database and pass it back to Flex as list of value objects, Flex, as usual, will convert it to ArrayCollection of value objects and bind it to the datagrid. If you want to filter out the record in the datagrid, you don’t need to remove the records from the ArrayCollection. All you need is to provide the implementation of the ArrayCollection’s filterFunction. Here is the example:
...
[Bindable]
private var _data:ArrayCollection;
private function init():void
{
_data = new ArrayCollection(
[
{ "name":"One" },
{ "name":"Two" },
{ "name":"Three" },
{ "name":"Four" },
{ "name":"Five" }
]);
_data.filterFunction = filterFunction;
}
private function filterFunction( item:Object ):Boolean
{
var name:String = String( item.name ).toLowerCase();
var searchStr:String = textInput.text.toLowerCase();
return searchStr == name.substr( 0, searchStr.length );
}
//trigger when user type in search string in the text box
private function handleChange():void
{
//this will iterate the dataset against the filterFunction
_data.refresh();
}
...
Here is the caveat. When using a filterFunction in an ArrayCollection, the Flash Player will always search every item. It is not an effective way. To speed it up, Hillel Coren has documented an approach that the subsequent filters will search on the filtered list instead. His approach is elegant and well-documented. Go to his article for detailed. The demo is here. I found Hillel solution quite elegant. Apart from making the search more efficient, his design also modulizes the search code so that it can be unit tested easily.
The idea is to keep the last failed search string in each record. So, if the next search string begins with last search string, it is unnecessary to check each fields in the record before you can say it won’t be matched.
//---- SearchDemo.mxml ---
private function filterFunction( item:Person ):Boolean
{
return SearchUtils.isMatch( item, textInput.text );
}
//--- SearchUtils.as ---
public static function isMatch( item:ISearchable, searchStr:String ):Boolean
{
if (_enableFasterSearch && !quickCheck( item, searchStr )){
return false;
}
var orSearchStrs:Array = searchStr.split( "," );
for each (var orSearchPart:String in orSearchStrs)
{
var andSearchStrs:Array = orSearchPart.split( " " );
var isMatch:Boolean;
for each (var andSearchStr:String in andSearchStrs){
isMatch = false;
for each (var field:String in item.getSearchFields()){
if (item.matchesField( field, andSearchStr )){
isMatch = true;
}
}
if (!isMatch){break;}
}
if (isMatch){
item.setLastFailedSearchStr( "" );
return true;
}
}
item.setLastFailedSearchStr( searchStr );
return false;
}
...
There are several things worth to mention here. The search routine is generic b/c it is against the item that implements the ISearchable Interface. The item implements this interface will provide the implementation of the matchesField method. So, the generic search routine can focus on providing features on top of it like ‘AND’, ‘OR’ filter and quick check algorithm.
NOTE: After you set the filterFunction, you will only get the filtered record if you iterate the ArrayCollection. If you want to get the full dataset, you need to do:
for each (var obj:Object in arrayCollection.source)
{
// do stuff
}
Again, thanks for Hillel’s tip.
I will talk about Data Export in Part 2 of this series.







No comments yet.