Anatomy of a filter control

Parent Previous Next

The Grid exposes a robust mechanism in which you can create and plug in your own filter controls.


Step1: Choose the appropriate IFilterControl Interface OR extend an existing FilterControl.


Filter Controls in most cases, need to implement a derivative of the IFilterControl interface (See the end of this article if none of the below interfaces apply to your scenario). This gives you the following options:




Now consider what you wish you filter control to behave like. Are you going to be executing Text based search? Then your best best is ITextFilterControl (Or extending TextInput). Are you performing Date Range Searches? Then you want the IRangeFilterControl. Do you need complete control over the FilterExpression that gets generated? Then you need the Dynamic Filter Control. Do you need even more control and have a requirement to build in custom logic in your filter control? Then you can use the ICustomMatchFilterControl.


Step 2:

Now, there are a bunch of properties and methods that the IFilterControl comes along with. Most of these are simply getters and setters, for example, below is the code from our TriStateCheckBox filter control:


   TriStateCheckBox=function(){


       flexiciousNmsp.UIComponent.apply(this,["span"]);

       //BEGIN FILTER PROPERTIES

       /**

        * Whether or not there is an active search

        * @property hasSearch

        * @type Boolean

        * @default false

        */

       this.hasSearch = false;

       /**

        * Whether or not this control has been registered. This should not be set by your code.

        * @property registered

        * @type Boolean

        * @default false

        */

       this.registered = false;

       /**

        * This is usually automatically set, you don't have to manually set it,

        * unless you're sending strings as Date objects. When set, will attempt

        * to first convert the current value to the type you specified and then

        * do the conversion.

        * Values : auto,string,number,boolean,date

        * @property filterComparisionType

        * @type String

        * @default auto

        */

       this.filterComparisionType = "auto";

       /**

        * The field to search on, usually same as the data field.

        * @property searchField

        * @type String

        * @default null

        */

       this.searchField = null;

       /**

        * The filter operation to apply to the comparison

        * See the FilterExpression class for a list.

        * Please note, for CheckBoxList and MultiSelectComboBox, this field

        * defaults to "InList" and is ignored when set.

        * Valid values are : "Equals,NotEquals,BeginsWith,EndsWith,Contains,DoesNotContain,GreaterThan,LessThan,GreaterThanEquals,LessThanEquals,InList,NotInList,Between"

        * @property filterOperation

        * @type String

        * @default Equals

        */

       this.filterOperation = null;

       /**

        * The event that the filter triggers on. Defaults to "change", or if the

        * filterRenderer supports com.flexicious.controls.interfaces.IDelayedChange, then

        * the delayedChange event.

        * @property filterTriggerEvent

        * @type String

        * @default change

        */

       this.filterTriggerEvent = "change";

       /**

        * The grid that the filter belongs to - can be null

        * if filter is used outside the grid

        * @property grid

        */

       this.grid = null;

       /**

        * The grid column that the filter belongs to - can be null

        * if filter is used outside the grid

        * @property gridColumn

        * @return

        */

       this.gridColumn = null;

       //END FILTER PROPERTIES


Step 3:


Dispatch the appropriate event: The Flexicious DataGrid is setup so that the filter control should dispatch an event (which is configurable) that the datagrid listens for, and when this event is dispatched, a filter is run. By default, this event is the "change" event. For controls that implement IDelayedChange, it is the delayed change event. So, this is something you need to keep in mind while implementing your own filter control. The change event being dispatched by default will trigger the filter. You can control this via setting the filterTriggerEvent on the column, if you want a specific event to trigger the filter. For example, our TristateCheckBox dispatches a delayed change, which basically lets the user flip through multiple states of the checkbox before running the filter, so a filter is not run on every state flip. Same thing applies to our TextInput Filter, so a filter is not run on every key stroke, rather it runs when the user pauses typing for a predetermined interval.


Below is the code that does this:


   //===========DELAYED CHANGE INCLUDE======================//

   TriStateCheckBox.prototype.dispatchEvent=function(event){

       if(this.enableDelayChange)

       {

           if(event.type == flxConstants.EVENT_CHANGE)

           {

               // if change event, intercept

               if (this.timerInstance == null)

               {

                   this.timerInstance = new flexiciousNmsp.Timer(this.delayDuration);

                   this.timerInstance.addEventListener(this,flxConstants.EVENT_TIMER_COMPLETE, this.onTimerComplete,false,0,true);

               }

               this.timerInstance.reset(); // reset if already set...

               this.timerInstance.repeatCount = 1;

               // starts the timer ticking

               this.timerInstance.start();

           }

       }

       return flexiciousNmsp.UIComponent.prototype.dispatchEvent.apply(this,[event]);

   };


   TriStateCheckBox.prototype.onTimerComplete=function (event){

       this.dispatchEvent(new flexiciousNmsp.FlexDataGridEvent("delayedChange"));

       this.timerInstance.stop();

       this.timerInstance=null;

   };

   //===========END DELAYED CHANGE INCLUDE======================//


Step 4:


Implement the Filter Interface methods: There are a few methods that we need to implement based on which filter interface we are implementing: For example ,if you are implementing IRangeFilterControl, we need to provide searchRangeStart and searchRangeEnd. This is then used by the filter logic to build a BETWEEN expression. If either searchRangeEnd or searchRangeStart is empty, the entire filter expression for this control is ignored. Please review the documentation of the particular interface to identify which methods you need to implement.


The other important methods are: setValue, getValue and clear. These methods are called by our API, when we rebuild the filter row in response to a refresh of the layout. So we call getValue, store it temporarily, destroy the filter component, create a new one, and then call setValue with the previously persisted filter Value. It is the responsibility of the filter control, to restore its state, from information it we give it in setFilterValue (which it gave us in getFilterValue). For example, below is the code from our TristateCheckBox that does this. Note that each filter control would have its own way of handling these methods.


   //==================IFilterControl Methods===============================/

   TriStateCheckBox.prototype.clear = function () {

       this.setSelectedState(flexiciousNmsp.TriStateCheckBox.STATE_MIDDLE);

   };

   /**

    * Generic function that returns the value of a IFilterControl

    */

   TriStateCheckBox.prototype.getValue = function () {

       return this.getSelectedState();

   };

   /**

    * Generic function that sets the value of a IFilterControl

    * @param val

    */

   TriStateCheckBox.prototype.setValue = function (val) {

       this.setSelectedState(val);

   };

   //==================End IFilterControl Methods===============================/




Step 5:


Using the Filter Control : Once we have a filter control implemented, its just a matter of setting its name (along with the package) in the filterRenderer property of the column.  


All of that said, there may be scenarios where you need to massage the value of the filter before processing it, or you may need to apply your own filtering logic to the filter control. There are ways to do this:


1) Implement IConverterControl: This interface is used, when you need to process the value of the item being compared. For example. A database sends down date columns as Strings. You have a DateComboBox as a filter. By default, the filter mechanism will try to compare two dates against a string. To combat this situation, you implement IConverterControl, and in the convert method, convert the string to a Date. We do this in the sample, look at MyDateComboBox.as (The code is commented, but its there).


2) Implement ICustomMatchFilterControl : This lets you completely encapsulate the logic for performing a filter. Please note, only use a FilterControl that implements this interface in filterPageSortMode=client.


Note : If you are writing your own custom filter controls, you MUST inherit from the flexiciousNmsp.UIComponent class. Please review the guide on understanding the structure of the UIComponent class.