filterextjspaginationgridlocal

How to apply filter function to paging grid with local(memory) store in ExtJS6?


I have a paging grid with local store, and I want to apply a filter using my own function. But it is failed.

From internet recommendations I used remoteFilter: true and enablePaging: true options in store config.

And it works perfectly if I filter store with specific configuration object:

store.filter([{ property: 'age', value: 12 }]);

unfortunately it is not enough to build complex filter criteria.

In accordance with documentation there is a special filterBy method in store object to use function as filter. But, when I am providing it like this:

store.filterBy( function( record ) {
  return record.get( 'age' ) <= 12;
});

I got an error Uncaught Error: Unable to use a filtering function in conjunction with remote filtering.

Here is my working example in fiddle https://fiddle.sencha.com/#fiddle/2u8l

This is my store configuration and all business logic from controller. I'll skip view configuration here to focus on main part( IMO )of code

Ext.define('TestGridViewModelr', {
    extend: 'Ext.app.ViewModel',

    alias: 'viewmodel.myexmpl.main.testgrid',

    data: {
    },
    formulas: {},
    stores: {
        simpsons: {
            model: 'Ext.data.Model',// 'SimpsonModel',
            pageSize: 2,
            // remoteSort: true,
            remoteFilter: true,
            proxy: {
                type: 'memory',
                enablePaging: true,
                reader: {
                    type: 'json',
                    rootProperty: 'items'
                }
            }
        }
    }

});

Ext.define('TestGridController', {
    extend: 'Ext.app.ViewController',

    alias: 'controller.myexmpl.main.testgrid',

    init: function () {
        console.log('controller inititalized\n  init async store loading...');
        setTimeout( this.onStoreLoad.bind( this ), 1000 );
    },

    initViewModel: function(vm){
        console.log( 'viewModel init', vm.get('test') );
    },
    emptyMethod: function () {},

    onStoreLoad: function () {
        console.log('loading store');
        var vm = this.getViewModel();
        var store = vm.getStore('simpsons');
        store.getProxy().data = this.getSimpsonsData().items;
        store.reload();
        // store.loadData( this.getSimpsonsData() );
    },

   //++++++++++++  FILTERING  ++++++++
    /* NO PROBLEM */
    onToggleFilter: function () {
        console.log('simple filter');
        var filter = this.getSimpleFilter()
        this.toggleFilter( filter );
    },
    /* PROBLEM */
    onToggleFnFilter: function(){
       console.log('function filter');
    //   var filterFn = this.filterChildren;
       var filterFn = this.getFilterUtil()
       this.toggleFilter( filterFn );
    },

    /* NO PROBLEM */
    getSimpleFilter: function(){
        return {
            property: 'age',
            value: '12'
        };
    },

    /* PROBLEM */
    getFilterUtil: function() {
        return Ext.create( 'Ext.util.Filter', {
            filterFn: this.filterChildren
        })
    },

    filterChildren: function( record ) {
        var age = record.get( 'age' );
        console.log( 'filter record up to age:', age )// debugger;
        return parseInt( age ) <= 12;
    },

    toggleFilter: function( fltr ) {
        var store = this.getViewModel().getStore( 'simpsons' );
        var filters = store.getFilters();
        if ( filters.length > 0 ) {
            store.clearFilter();
        } else {
           this. applyFilterToStore( fltr, store );
        }
    },

    applyFilterToStore: function( filter, store ){
        var method = Ext.isFunction( filter ) || filter instanceof Ext.util.Filter
            ? 'filterBy'
            : 'setFilters';
        store[method]( filter );
    },



    getSimpsonsData: function(){
        return  {
            'items': [{
                'name': 'Lisa',
                'age': 12,
                "email": "lisa@simpsons.com",
                "phone": "555-111-1224"
            }, {
                'name': 'Bart',
                'age': 8,
                "email": "bart@simpsons.com",
                "phone": "555-222-1234"
            }, {
                'name': 'Homer',
                'age': 40,
                "email": "homer@simpsons.com",
                "phone": "555-222-1244"
            }, {
                'name': 'Marge',
                'age': 34,
                "email": "marge@simpsons.com",
                "phone": "555-222-1254"
            }]
        }
    }
});



In general I want to have ability to set up filter criteria on paging grid with local store programmatically. Function allows me to extend filter capabilities and build flexible logical expression using conjunction and disquisition. For example:

name.lenght <= 4 &&  ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)

Thank you in advance, A.


Solution

  • I have finally resolved this issue!

    Mentioned error raised in onFilterEndUpdate method of store in next lines:

    ...
    me.getFilters().each(function(filter) {
      if (filter.getInitialConfig().filterFn) {
        Ext.raise('Unable to use a filtering function in conjunction with remote filtering.');
      }
    });
    ...
    

    I have override this method in my store entity and commented out these lines. I know it is not best solution, but I could not find better one.

    Here is the complete solution concerning this topic:

    1. Configure store with remoteFilter: true and enablePaging: true options:
    {
     model: 'Ext.data.Model',
     pageSize: 2,
     remoteFilter: true,
     proxy: {
       type: 'memory',
       enablePaging: true,
       reader: {
         type: 'json'
       }
     }
    }
    
    1. Load data into the store using its Proxy instead of loadData method:
    store.getProxy().data = this.getSimpsonsData().items;
    store.reload();
    
    1. Override method onFilterEndUpdate after store initialization and comment out mentioned lines i.e:
    onStoreLoad: function() {
    ...
      store.onFilterEndUpdate = this.onFilterEndUpdate.bind( store );
    ...
    },
    
    onFilterEndUpdate: function() {
      var me = this
        , suppressNext = me.suppressNextFilter
        , filters = me.getFilters(false);
      // If the collection is not instantiated yet, it's because we are constructing.
      if (!filters) {
        return;
      }
      if (me.getRemoteFilter()) {
        // me.getFilters().each(function(filter) {
        //      if (filter.getInitialConfig().filterFn) {
        //          Ext.raise('Unable to use a filtering function in conjunction with remote filtering.');
        //      }
        // });
        me.currentPage = 1;
        if (!suppressNext) {
          me.load();
        }
      } else if (!suppressNext) {
        me.fireEvent('datachanged', me);
        me.fireEvent('refresh', me);
      }
      if (me.trackStateChanges) {
        // We just mutated the filter collection so let's save stateful filters from this point forward.
        me.saveStatefulFilters = true;
      }
      // This is not affected by suppressEvent.
      me.fireEvent('filterchange', me, me.getFilters().getRange());
    }
    

    Here is live example in fiddle https://fiddle.sencha.com/#fiddle/2ub7