Latest posts by Nenad (see all)
- Traquer – Testing made easy - October 25, 2016
- ExtJS tag cloud - June 28, 2016
- Interactive Twitter stream visualization now online! - June 16, 2016
Recently I came around the situation in which it was required to provide a set of default filters that should be applied to grid during it’s initialization stage so it comes filtered by some specific rules.
ExtJS has it’s ways of doing so and it’s really easy to add a filter to grids column:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
Ext.define('Examle.grid.Simpsons', { extend: 'Ext.grid.Panel', alias: 'simpsonsGrid', title: 'Simpsons', store: 'SimpsonsStore', columns: [ { text: 'Name', dataIndex: 'name' }, { text: 'Email', dataIndex: 'email', flex: 1 }, { text: 'Phone', dataIndex: 'phone' }, { text: 'Status', dataIndex: 'status', filter: { type: 'list', options: [ { id: 'online', text: 'Online'}, { id: 'offline', text: 'Offline'} ], operator: 'in', value: ['online', 'offline'], property: 'status' } } ], height: 200, width: 400 }); |
Take into account that this sample grid uses remote store (but this will also work with local once).
As you can see on lines from 27-29, default filter value has been set to display both statuses ‘online’ and ‘offline’ but you can set either value separately.
The problem with this approach occurs when you want this filter values set dynamically. More precise – I wanted to route my application with specific set of filter rules set in url, like:
1 |
http://www.example.com/#simpsons/status/active |
or
1 |
http://www.example.com/#simpsons/name/bart |
I won’t explain in detail the ways of routing in ExtJS, you can read more about it here (Controlling an Application with Router).
Routing
For what it concerns our scenario here, let’s assume that my routing to above-mentioned grid uses this logic:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Ext.define('Example.view.SimpsonsController', { extend : 'Example.app.SimpsonsController', routes : { 'simspons/:filter/:value' : 'onSimpsons' }, onSimpsons : function(filter, value) { var mainContainer = Ext.getCmp('mainContainer'); mainContainer.add({ xtype: 'simpsonsGrid' }); } }); |
As you can see, I’m passing two arguments to route handler, filter and value, but I’m not using them at this moment just to show you the routing part.
Next I’m going to modify this handler and add default filters that are going to be processed with a fine plugin.
1 2 3 4 5 6 7 8 9 10 11 12 |
onSimpsons : function(filter, value) { var mainContainer = Ext.getCmp('mainContainer'); mainContainer.add({ xtype: 'simpsonsGrid', defaultFilters: [{ operator: 'in', value : value, property: filter }] }); } |
As you can see I used filter and value arguments and passed them to defaultFilters object’s properties in our grid constructor, note that I haven’t used Ext.util.Filter method since it’s not needed. Also in real world you would like to check if those arguments are passed, etc.
Now what we need to do is to write out our plugin that will handle this approach and than assign it to grid.
The plugin
The plugin will extend Ext.grid.filters.Filters class in one small detail in init method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
Ext.define('Example.grid.DefaultFilters', { extend: 'Ext.grid.filters.Filters', alias: 'plugin.defaultfilters', pluginId: 'defaultfilters', init: function(grid) { var me = this, store, headerCt; me.grid = grid; grid.filters = me; if (me.grid.normalGrid) { me.isLocked = true; } grid.clearFilters = me.clearFilters.bind(me); store = grid.store; headerCt = grid.headerCt; headerCt.on({ scope: me, add: me.onAdd, menucreate: me.onMenuCreate }); grid.on({ scope: me, destroy: me.onGridDestroy, reconfigure: me.onReconfigure }); me.bindStore(store); if (grid.stateful) { store.statefulFilters = true; } grid.on({ destroy: function() { if (me.store && me.store.getFilters()){ me.store.getFilters().un('remove', me.onFilterRemove, me); } }, beforerender: function(){ // NOTE: this will load store for grids that are currenlty // rendering and resume suspended events so we can use our // grid normally. this.store && this.store.load(); this.store.filters.resumeEvents(true); } }); if (Ext.isArray(grid.defaultFilters)){ grid.store.filters.suspendEvents(); grid.store.filters.add(grid.defaultFilters); var i = 0, columns = grid.columns; for (i = 0, len = columns.length; i < len; i++) { var column = columns[i], defaultFilter = grid.defaultFilters.filter(function(df){ return df.property == column.dataIndex })[0]; if(defaultFilter){ Ext.merge(column.filter, defaultFilter) } } } // initColumns after we added properties from defaultFilters to // our respective columns me.initColumns(); } }); |
As you can see in the code above, we’re checking whether the grid has defaultFilters property set and if it has one, we’ll iterate through each column to check if it’s filter has been set. If there is one, we’ll merge constructors of column’s filter property and our defaultFilter.
At the end, the plugin will initialize columns and render them with default properties set accordingly.
One more thing to do is to assign this plugin to our grid and we’re ready to rock:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
Ext.define('Examle.grid.Simpsons', { extend: 'Ext.grid.Panel', alias: 'simpsonsGrid', title: 'Simpsons', store: 'SimpsonsStore', plugins: [{ ptype: 'defaultfilters', pluginId: 'defaultfilters' }], columns: [ { text: 'Name', dataIndex: 'name' }, { text: 'Email', dataIndex: 'email', flex: 1 }, { text: 'Phone', dataIndex: 'phone' }, { text: 'Status', dataIndex: 'status', filter: { type: 'list', options: [ { id: 'online', text: 'Online'}, { id: 'offline', text: 'Offline'} ], operator: 'in', value: ['online', 'offline'], property: 'status' } } ], height: 200, width: 400 }); |
Voila.