Filtering ExtJS data stores is stupid-simple. When filtering, you have 2 main options:

  • Filtering on a property (e.g., “field”)
  • Filtering with a custom function
So let’s take a quick look at each one of these.
First, here’s our Model and Store:
Ext.define('Person', {
    extend: 'Ext.data.Model',
    idProperty: 'id',
    fields: [
         {'firstname', type:'string'},
         {'middlename', type: 'string'},
         {'lastname', type: 'string'}
    ]
});
Ext.create('Ext.data.Store', {
    autoLoad: true,
    model: 'Person',
    storeId: 'PersonStore',
    proxy: {
         type: 'ajax',
         url: 'myurl.json',
         reader: {
              type: 'json',
              root: 'people'
         }
    }
});
Ok, now that we have those set up, let’s say that we want to filter our results to only those people whose last name contains “son” (like “Watson”). Using the property filter, we can do this very simply:
var store = Ext.data.StoreManager.lookup('PersonStore');
var peoplefilter = new Ext.util.Filter({
     property: 'lastname',
     value: 'son',
     anyMatch: true,
     caseSensitive: false,
     root: 'data'
 });
 store.filter(peoplefilter);

Nothing to it, right? There are two important parts here. First, for “property”, we are simply specifying the field in our Model which we want to filter on…in this case, “lastname.” Second, we specify that we want the filter to look at the “data” object of each record–otherwise, it will fail miserably 🙂

For simple filtering, using the “property” method will get you by pretty good. For more advanced filtering, however, you’ll need a filter function. A filter function is simply what it sounds like–a custom function that is run against each record. If the function returns true, the record is included in the results, and is excluded otherwise.

So let’s say that we want to only return people whose last names begin with “Mc”. Our filter function might look like this:

var store = Ext.data.StoreManager.lookup('PersonStore');
var peoplefilter = new Ext.util.Filter({
    filterFn: function(item) {
        return item.data.lastname.substr(0, 2)=='Mc' ? true : false
    }
});

While a little different than using the property method, filterFn is very easy to use. You can simply do custom processing on each item, and determine (via returning true or false) whether the record should be part of the filtered set or not. Pretty simple.

Passing Multiple Fields to the Property Filter

Now consider this: you have a larger set of fields in your model, and you want to do something of a wild card search against a number of fields in your model. How would you accomplish this?

Unfortunately, the property filter does not out-of-the-box support passing multiple fields. True enough, you could add 1 filter for each field, but this would leave you with something of an X AND Y AND Z result, instead of the desired X OR Y OR Z result. You could, of course, figure all of this out in a custom filter function. Here’s an example of that:

var searchfilter = new Ext.util.Filter({
     filterFn: function(item){
         var searchtest,fnmatch,mnmatch,lnmatch;
         var escapere = Ext.String.escapeRegex;
         searchtest = new RegExp(escapere(search), 'i');
         // check match on first name
         fnmatch = searchtest.test(item.data.firstname);
         // check match on middelname
         mnmatch = searchtest.test(item.data.middlename);
         // check match on name
         lnmatch = searchtest.test(item.data.lastname);
         // if any of the fields matched, return true, which will include  record in filtered set
         if(fnmatch || mnmatch || lnmatch) {
             return true;
         }
         // otherwise, return false, which will exclude record from filtered set
         else {
              return false;
         }
    }
})

While not particularly pretty, it works. The main problem is that it more or less duplicates what the property filter is already doing, just a in a bit clunkier way. So instead of doing this, a better approach might be to override the default implementation of Ext.util.Filter. Instead of only allowing for a single property to be evaluated, we’ll beef it up a bit to accept either an array or string of properties.

Ext.override(Ext.util.Filter,{
    createFilterFn: function() {
        var me       = this,
            matcher  = me.createValueMatcher(),
            property = !Ext.isArray(me.property) ? me.property.split(',') : me.property
        return function(item) {
            var hasmatch = false;
            for(var i=0;i<property.length;i++) {
                if(matcher.test(me.getRoot.call(me, item)[property[i]])) {
                    hasmatch=true;
                    break;
                }
            }
            return matcher === null ? value === null : hasmatch;
       };
    }
})

This is a bit cleaner, IMO. Plus, if we ever need to pass multiple fields to test again, we’ve already got this in place to handle it. Finally, if you compare this to the original, you’ll notice that very little has been changed, and that it’s been designed to work agnostic of whether a single or multiple properties are passed.