Apply grid filter programmatically from function

28,162

Solution 1

I have workaround:

bbar: [{
    text: 'Do a filter',
    handler: function() {
        var grid = this.up('grid');
        var dateValue = Ext.Date.parse(aFilter.value, 'Y-m-d H:i:s');
        var value = aFilter.comparison == 'gt' ? {after: dateValue} : {before: dateValue};

        var gridFilter = grid.filters.getFilter(aFilter.field);

        if (!gridFilter) {
            gridFilter = grid.filters.addFilter({
                active: true,
                type: aFilter.type,
                dataIndex: aFilter.dataIndex,
            });

            gridFilter.menu.show();
            gridFilter.setValue(value);
            gridFilter.menu.hide();
        } else {
            gridFilter.setActive(true);
        }

        Ext.Function.defer(function(){
            gridFilter = grid.filters.getFilter(aFilter.field);
            gridFilter.setValue(value);
        }, 10);
    }
}]

As you can see I actually apply filter 2 times.

Solution 2

As an update, I expanded this function and modified it to work with ExtJS 4.1.1

Here is an example of the function to set grid filters dynamically (without the user needing to click on the menu items). Afterwards, the filtered items will be visible to the user in the grid column header menus as if he clicked on them and set them manually.

The "grid" argument is a grid with FiltersFeature that you want to filter. The other argument is an array of "filter" objects (I'll show an example below), the function simply applies all the passed "filter" objects to the grid.

doGridFilter: function(grid, filters) {

    // for each filter object in the array
    Ext.each(filters, function(filter) {
        var gridFilter = grid.filters.getFilter(filter.field);

        gridFilter.setActive(true);
        switch(filter.data.type) {

            case 'date':
                var dateValue = Ext.Date.parse(filter.data.value, 'm/d/Y'),
                    value;

                switch (filter.data.comparison) {

                    case 'gt' :
                        value = {after: dateValue};
                        break;
                    case 'lt' :
                        value = {before: dateValue};
                        break;
                    case 'eq' :
                        value = {on: dateValue};
                        break;
                }
                gridFilter = log.filters.getFilter(filter.field);
                gridFilter.setValue(value);
                gridFilter.setActive(true);
                break;

            case 'numeric':
                var value;

                switch (filter.data.comparison) {

                    case 'gt' :
                        value = {gt: filter.data.value};
                        break;
                    case 'lt' :
                        value = {lt: filter.data.value};
                        break;
                    case 'eq' :
                        value = {eq: filter.data.value};
                        break;
                }
                gridFilter = log.filters.getFilter(filter.field);
                gridFilter.setValue(value);
                gridFilter.setActive(true);
                break;

            case 'list':
                gridFilter = log.filters.getFilter(filter.field);
                gridFilter.menu.setSelected(gridFilter.menu.selected, false);
                gridFilter.menu.setSelected(filter.data.value.split(','), true);
                break;

            default :
                gridFilter = log.filters.getFilter(filter.field);
                gridFilter.setValue(filter.data.value);
                break;
        }
    });
}

Here's an example of a "filter" object array.

// an example of a "filters" argument
[{
    field: 'some_list_column_data_index',
    data: {
        type: 'list',
        value: 'item1,item2,item3,item4,item5,item6,item7'
    }
}, {
    field: 'some_date_column_data_index',
    data: {
        type: 'date',
        comparison: 'gt',
        value: '07/07/2007'
    }
}]

One caveat, you need to "create" the filters manually before using this function. Normally FiltersFeature grid filters are "created" the first time a user clicks on one of them, that may not happen if the user just wants to apply one of these predefined filters.

That can be handled easily by including this afterrender listener in the gridpanel.

listeners: {

    // must create the filters after grid is rendered
    afterrender: function(grid) {
        grid.filters.createFilters();
    }
}

Solution 3

Just add

filter: true

to grid columns description like this:

me.columns = [
        {header:"Name", dataIndex:"name", editor:"textfield", filter: true},
    ];

if you want to get the filter work after the first attempt, first instance create.

Solution 4

Here is something that may be worth looking into. It seems that the filters plugin is listening for menucreate event to initialize the filters. I wonder if menu create event is deferred until necessary and hence the filters don't get initialized?

/**
 * @private Handle creation of the grid's header menu. Initializes the filters and listens
 * for the menu being shown.
 */
onMenuCreate: function(headerCt, menu) {
    var me = this;
    me.createFilters(); //<------
    menu.on('beforeshow', me.onMenuBeforeShow, me);
},
Share:
28,162
egerardus
Author by

egerardus

Updated on April 23, 2020

Comments

  • egerardus
    egerardus about 4 years

    Using Ext.ux.grid.FiltersFeature, I have remote filters and I am trying to write a function to apply a date filter on a grid column programmatically (rather than clicking on the filter drop down menu in the column header). The first time I run the function the grid store gets reloaded without the filter. When I run the function a second time (and every time thereafter) it works totally fine, the store reloads with the filters. Here is the gist of the function I have:

    // a filter object for testing
    aFilter = {type: 'date', field: 'a_date_field', comparison: 'gt', value: '2012-03-08 00:00:00'}
    
    var grid = Ext.create('Ext.grid.Panel', {
        store: store,
        features: [{
            ftype: 'filters',
        }],
        columns[{
            header: 'ID',
            dataIndex: 'id',
            itemId: 'id',            
            width: 40,
        }, {
            xtype: 'datecolumn',
            header: 'Date',
            dataIndex: 'a_date_field',
            itemId: 'a_date_field',
            width: 75,
            format:'j-M-Y',
            filterable: true
        }],
        listeners: {
            'afterrender': function() {
    
                // Need to create the filters as soon as the grid renders
                // rather than waiting for the user to click on the header
                grid.filters.createFilters();
            }
        },
        bbar: [{
            text: 'Do a filter',
            handler: function() {
    
                // get the filter that is attached to the grid
                var gridFilter = grid.filters.getFilter(aFilter.field);
    
                // have to do this to create a menu for this filter
                gridFilter.init({dataIndex: aFilter.field, type: aFilter.type, active: true});
    
                // if this column is a date filter column
                if (gridFilter.type == 'date') {
                    var dateValue = Ext.Date.parse(aFilter.value, 'Y-m-d H:i:s');
                    if (filter.comparison == 'gt') {
                        gridFilter.setValue({after: dateValue});
                    } else {
                        gridFilter.setValue({before: dateValue});
                    }
                }
            }
        }
    });
    

    I also found that this function works the first time if I click on any grid header menu before I run the function.

    I've been trying to find out what changes are made to the grid which make the filter work after the first attempt fails or what clicking on any grid header does to make it work. But nothing I add seems to fix it so it will run the first time. Has anyone implemented this successfully?

  • egerardus
    egerardus about 12 years
    Thanks Dmitry, I did try running this manually but I didn't seem to do the trick. I also got the beforeshow event triggered after it attaches it here but no luck. It was bugging on the menu though, physically showing and hiding it as Lolo suggested handled it.
  • egerardus
    egerardus about 12 years
    Users will continue to filter the grid in other ways after applying this filter so I need to keep the grid header synchronized with the filter.
  • dbrin
    dbrin about 12 years
    Wow @Lolo how did you arrive at the apply filter twice solution? Is there a bug that's filed against it?
  • Krzysztof
    Krzysztof about 12 years
    @DmitryB I came to the point where it worked after second click of the button. So the next obvious step was to try do it at once. I don't know if this is a bug, probably it is. Setting the value causes accessing the menu, so it must be rendered. In the end it seems to me that this first setValue call (inside if) isn't necessary
  • MarkyRoden
    MarkyRoden about 11 years
    I am using 4.1 and 4.2 the following returns an error "filter.data.value.split(',') //is not a function" - filter.data.value seems to work just fine on its own
  • cjauvin
    cjauvin about 11 years
    I'm currently using 4.1.1 and it seems that using Ext.Function.defer is superfluous: they key seems to be to call filter.setValue twice, and importantly, to renew the filter handle in between, i.e. filter = grid.filters.getFilter(..) (is this a bug?)
  • reergymerej
    reergymerej almost 11 years
    Setting the value twice is pretty hacky. Just ensure the grid's view's header menu has been initialized.