The existing filtering methods used with the Flex DataGrids seem to be very UI intensive. In this implementation, a simple "search style" filtering is made available. I believe this provides a more intuitive filtering functionality, and as I saw in the implementation, extending the filtering capabilities is quite easy. At the same time, it allows the UI designer to be more creative in providing options and feedback to the user.
Using the Demo
The demo is provided as an SWF file. You need to have Flash Player 9 installed for your browser. Then you can drop in the SWF file into a browser window and test it.
To start using the application, you can just start typing in the filter text box. Type 'mi' or '12' and see what happens.
Or to do a column specific search, type '(name . yo)' or '(name ^ j)' or '(name ~$ s)' or '(phone !. 21)' ... and well, keep going.
Using the Demo Continued ...
In the current implementation, I have provided two modes. If the search text starts with anything other than a
(, then the filtering method used is a simple filtering which looks for the given text in any of the columns. If the first character is a
(, then in the current implementation, it defaults to the columnar search mode.
When you start typing an open bracket, (, the current implementation will guide you, via ToolTips, to enter the remaining filter text.
The operators that can be used are customizable in code. When you reuse this code, you can choose what your likely operators are. I have used the following:
!= are used as normally used - greater than, less than, equal to, not equal to, respectively. The ! is used to indicate all inverses or NOT-ing. These are string comparisons.
!. are used as 'contains' and 'does not contain' operators.
$ are used as the 'begins with' and 'ends with' operators, respectively. Each of them can be 'NOT'-ed, i.e.,
!^ is 'does not begin with' and
!$ is 'does not end with'.
~ is for case sensitive filtering. E.g., between
(Name ~^ Ben) and
(Name ^ Ben), the first is a case sensitive search and the second is case insensitive.
In addition, I intend to use these characters too, but I'm yet to code them. (This will require a little more thinking :) ).
# for numeric comparison. E.g., between
(Name #> 025) and
(Name > 025), the first does a numeric comparison and the second does a text comparison. (I've written this for now and seems to work, but I don't really know why. It's probably due to some quirk in ActionScript that I do not know of. It is defaulting to some behavior, I don't know what it is, so should either find out why it works or make it work in a known way.)
Note: At this point, the Regular Expression for matching the operators is hard-coded, and if you are adding a different set of operators, this needs to be changed in the
case OPERATOR: area within ColumnOperatorValueParser.as.
Using the Code
The code is quite simple and there doesn't appear to be any reason to copy paste parts of it here. However, a flow of activity explanation would be useful. At a high level, the relevant part of the code has this flow:
- Create a new parser with the allowed set of column names, which can be obtained from the datagrid itself and the allowed set of operators, which you can choose. During parsing, when a potential column on an operator token is found, it is compared against the available list of allowed values.
- Wait for user to type something.
- Parse the changed input text (or expression).
- If an invalid expression is encountered, catch that event (
EMPTY_STRING) and display a useful comment/feedback to the user.
- If a proper expression is provided, you can obtain a list of the parsed expressions in a tuple like this:
[column_name, operator, search_value]. The class that represents this is
- When a proper expression is encountered, the parser
ColumnOperatorValueParser raises a
FilterStringEvent of type
- When a proper list of parsed expressions is available, the data can be filtered by using the normal
<data id>.refresh() calls.
- Typically, you can do a
switch() on the
ColumnOperatorValueTriplet operator and perform a comparison operation to suit the type of data using the
search_value fields in
Points of Interest
The Parsing Algorithm
The parsing algorithm that I've put together is quite elementary. A typical expression statement is expected to be of this form:
((EOL) | (BEGIN_CHAR & COLUMN_NAME & OPERATOR & SEARCH_VALUE & (SEPARATOR_CHAR | EOL)))
For example: (firstname = ben)(age > 25)
Some notable points:
- Firstly, the disclaimer, I don't think the parsing algorithm is working perfectly. :-) This is the first time I'm coding in ActionScript and Flex where the code is not a variation of something copied from the net and modified to check something, and I've not quite figured out how to even debug properly in FlexBuilder!! Imagine. Well, anyways, long story short, it works for now, and I'd be happy to incorporate feedback to make it better.
Now that having been said, here is what would be good for you to know ...
- There is a
NextExpectedTokenList which contains the list of possible next tokens. This has to be an ordered list. If the token type that is first in the list is not encountered, only then is the next one checked for.
- The token to be checked next is maintained in
- At certain points, where the | symbol is, one of many possibilities exist for the next field - this will result in more than one entry in
NextExpectedTokenList. If none of them are encountered, then there is an error in the input string.
- For the remaining where the & symbol is, only one possibility exists, and if it is not encountered, then there is an error in the input string.
- When a particular token is expected, a Regular Expression matching is done. Column names typically start with an alphabet and can contain alphanumerics and the underscore. The operator has to be one in the given list; the Regular Expression for this is currently hard-coded to use some of the known characters. The search value can be anything but a close parenthesis.
- When a proper token is encountered, the
parsedExpressions array containing a list of
ColumnOperatorValueTriplet items is created.
- when a
COLUMN_NAME is encountered, a new
ColumnOperatorValueTriplet item is created.
- when an
OPERATOR is encountered, the operator is added to the last
ColumnOperatorValueTriplet that was added.
- when a
SEARCH_VALUE is encountered, the value is added to the last
ColumnOperatorValueTriplet that was added.
- When the expected tokens are not encountered or the input string is empty, appropriate
FilterStringEvent events are raised.
- When the parsing ends without error, a
FilterStringEvent is raised.
- The application handles the events and performs whatever actions it thinks fit. Ideally, filtering needs to be done in the front end only on
PARSING_COMPLETE. On anything else, it is enough to provide some feedback, allowing the user to correct the input string.
I have done two filter function implementations to provide an example and a ready set for anybody reusing this code. These are available as static functions within
ColumnOperatorValueParser. These may be reused and/or modified as you might require for your application.
simpleFilterData: this checks for the input string in any of the columns in the given data. The check done is 'contains'.
columnarFilterData: this uses the parsed expressions to do a comparison based on some defined operators.
User Feedback on Events for Entering Expressions
In the current implementation, the events are used to provide appropriate feedback to the user. This will allow the user a smaller learning curve and also provide good information on what is expected.
TODOs and Future Directions
/ for date comparison. E.g., between
(Name /> 10/5/2009) and
(Name > 10/5/2009), the first does a date comparison and the second does a text comparison. This is not implemented yet!
- When entering in the search bar, there could be suggestions given - via autocomplete, or through popup combo boxes with the possible entries.
- Provide a user to 'filter by this cell' - a user would choose a cell (say right click and choose) and then the entry will automatically be put into the filter bar. This can also be done on drag and drop of a cell to the filter bar.
- When providing the search values in the expression, currently all characters except the right parentheses,
), are acceptable. However, there is a way to do validation in the search value - for example, if the data type of the column is a number, then only numbers need be allowed. Similarly for dates, email addresses, zip code, phone numbers, etc., if required.
- 19th October 2009 - Current version is the first.