Flex: How To Filter a View On User Input
Getting back to the accounts demo - the eagle-eyed might have noticed I added a "search" box above the view. As you can see below, as you type in a string the content of the DataGrid/view change to show only the entries matching that string.
It's not really searching, as such. I guess it's more like filtering than searching.
How Does It Do That?
As with lots of things Flex it is relatively simple to filter a data grid. The grid's content comes from its "dataProvider". In this case that's the XML returned from Domino to represent each document in the database. The XML looks like this:
<documents> <document unid="CB609D90B0103EF886257623007EBFC5"> <date>09/01/2009</date> <description>Nuka Cola</description> <direction>incoming</direction> <category>Sponge</category> <amount>1025.00</amount> </document> <document unid="90479C82FDDFB71186257624000A7CBE"> <date>09/01/2009</date> <description>Sexy nightwear</description> <direction>outgoing</direction> <category>Clothes</category> <amount>3333.00</amount> </document> .... </documents>
What we need to do is look at the XML and produce a subset of it, which contains the string being filtered on. Not as difficult as you might think. You don't even have to do any looping! It can all be done with E4X.
If you're going to get in to Flex then you really need to get yourself familiar with both how it handles XML and how to use E4X.
To find matching rows from the above XML format we can use the following ActionScript:
var token:String = fldFilter.text.toUpperCase(); var hits:XMLList = xmlDocuments.( child('amount').toUpperCase().indexOf(token) === 0 || child('description').toUpperCase().indexOf(token) != -1 ); if (hits.length() > 0) { viewExpenses.dataProvider = hits; }
What this does is take the string the user entered (in to a TextInput element with id "fldFilter") and looks inside the xmlDocuments object (which represents the XML for the grid) for matches. To find matches it uses the E4X syntax to compare the values of certain child elements. In this case we're look for document with a description that contains the string anywhere in it or where the amount matches the input exactly.
Once we have our slimmed-down XML we can apply it as the dataProvider of the grid, whose ID is "viewExpenses".
Simple, no?
I've missed out the key-down handling logic, which sets a timer to know when you're done typing etc but you get the point? What I'm wanting to show is how easy it is to use E4X to subset the XML in a DataGrid/view.
Tomorrow I'll show how to take it one step further and add a little wow factor at the same time.
Proper Searching?
As noted, this isn't really searching the database and the filter box isn't meant to replace any search box which might end up appearing at the very top right of the page.
In the accounts app the view just happens to show all documents in one go, so the filtering can be expected to behave a little like searching. If you're grid doesn't show all backend documents then you might want to think about how you convey this to the user. Alternatively you might want the filter box to send a request back to the server to do a search/filter there and fetch back a completely new set of XML. Again, not hard to do.
Related Reading
- The search box itself is based on the "fancy search field" I talked about a few weeks back.
- The key-press-handling timer code for performing the filtering as you type is borrowed from this org chart demo.
Great Tip , E4X is new to me . thanks for this info.
Nice post. This will come in handy. Thanks much!
Yep, great post. Thanks
The "right" way to do this is to use Collections and the filtering built into those. Basically you create an XMLListCollection to use as your data provider which wraps your doucments - eg. provider = new XMLListCollection(xmlDocuments.document); Then you use the filterFunction property of a collection (which comes from the ICollectionView interface - ArrayCollection has this same interface too) to create a filtering function.
The filtering function takes an item (ie. a single XML node) and returns a boolean whether it should be included or not. In this function you do your test against the value of the text field.
When the text field changes you just call provider.refresh() which causes the filtering function to be reapplied.
Here's a quick and dirty example I wrote just to make sure it works.. http://pastebin.com/m3c448f34
Hi Marcin. Thank you! I didn't know about that method. Much cleaner! Going to retrofit it to my code as soon as I get the chance.
Your approach fixes an issue with my approach, whereby edited a "document" updated the selectedItem in the "view"'s dataProvider. If you edited a document when looking at the filtered view it would appear to update in the front end but when you clear the filter it reverts to the original dataProvider (a different XML object) where the values remain unchanged. Naturally a full refresh of the app would bring the saved values from the server, but I guess your approach avoids this?
Thanks again, I love to learn best/better practice as I go. At least now we've given people two approaches and more food for thought...
Jake
I've been doing Flex for over 2 years and only really discovered that recently when studying for my certification exam. :)
There's a whole bunch of fancy stuff you can do with collections, data grids and Data Services where a lot of the paging and data fetching is auto-magic. Might be possible to do something with Domino via Java and BlazeDS, but that's getting into non-trivial territory!
Hey,
i created a Flex Library (DataFilterLib) that take care of all the filtering process, completly in MXML.
This library is free, you can find the project details there:
http://code.google.com/p/flex-datafilterlib/
If you want to have a look at the examples, they are all there (source available):
http://www.flex-tutorial.fr/DataFilterLib/examples/
Check the examples online if you want to see how to filter on multiple criterias, using different Flex UI Components (CheckBox, Slider, List, ...).
Thanks,
Fabien
http://www.flex-tutorial.fr
My Datagrid has an Arraycollection not an XML as dataprovider.
How can i implement this?
Thank you!
Excellent Work...How to get this code....