I have already wrote how to implement paging toolbar with filtering feature. Here I will show how to implement filter in form of plugin to use it with any toolbar of the grid.
Of course your toolbar can already have other GUI elements so I have implemented it with support of the placeholders. In the following sample I have a grid reload button and two filter elements: menu button and filter text field will be place after it in between delimiters.
initComponent: function () { this.items = [ this.getReloadButton(), '-', '**FILTER_SPLIT_BUTTON**', '**FILTER_TEXT_FIELD**', '-' ]; this.callParent(); },
And I am not forgetting the initialization of the plugin:
plugins: { filterplugin: { } }
The plugin code:
Ext.define('App.ux.grid.toolbar.plugin.Filter', { extend: 'Ext.plugin.Abstract', alias: 'plugin.filterplugin', filterItemWidth: 100, filterOnType: true, filterSplitButtonGlyph: 'xf0b0@FontAwesome', filterSplitButtonText: "Filter", filterSplitButtonPlaceHolder: '**FILTER_SPLIT_BUTTON**', filterTextFieldPlaceHolder: '**FILTER_TEXT_FIELD**', init: function (toolbar) { this.placeFields(); toolbar.on('afterrender', this.updateSearchColumnsMenu, this); }, placeFields: function () { this.replacePlaceHolderByCmp( this.filterSplitButtonPlaceHolder, this.getFilterSplitButton() ); this.replacePlaceHolderByCmp( this.filterTextFieldPlaceHolder, this.getFilterTextField() ); }, replacePlaceHolderByCmp: function (placeHolder, cmp) { var placeHolderIndex = this.getIndexByPlaceHolder(placeHolder), placeHolderCmp = this.getPlaceHolderCmp(placeHolder); this.getCmp().remove(placeHolderCmp); this.getCmp().insert(placeHolderIndex, cmp); }, getIndexByPlaceHolder: function (placeHolder) { return this.getCmp().items.findIndex('text', placeHolder); }, getPlaceHolderCmp: function (placeHolder) { var cmp = this.getCmp().down('[text="' + placeHolder + '"]'); return cmp; }, getFilterSplitButton: function () { if (!this.filterSplitButton) { this.filterSplitButton = Ext.create('Ext.button.Split', { text: this.filterSplitButtonText, handler: this.doFilter, glyph: this.filterSplitButtonGlyph, scope: this }); } return this.filterSplitButton; }, getFilterTextField: function () { if (!this.filterTextField) { this.filterTextField = Ext.create('Ext.form.field.Text', { submitValue: false, isFormField: false, width: this.filterItemWidth, margin: '-1 2 3 2', enableKeyEvents: true }); if (this.filterOnType) { this.filterTextField.on('change', this.doFilter, this); } else { this.filterTextField.on('specialkey', function () { if (e.getKey() == e.ENTER) { this.doFilter(); } }, this); } } return this.filterTextField; }, getSelectAllColumnsMenuItem: function () { if (!this.selectAllColumnsMenuItem) { this.selectAllColumnsMenuItem = Ext.create('Ext.menu.CheckItem', { text: "All Columns", checked: true, listeners: { checkchange: { fn: function (menuCheckItem, checked) { this.getFilterColumnsMenu().items.each(function (item) { if (item.dataIndex) { item.setChecked(checked); } }); this.doFilter(); }, scope: this } } }); } return this.selectAllColumnsMenuItem; }, getFilterColumnsMenu: function () { if (!this.filterColumnsMenu) { this.filterColumnsMenu = Ext.create('Ext.menu.Menu', { items: [ this.getSelectAllColumnsMenuItem(), { xtype: 'menuseparator' } ] }); } return this.filterColumnsMenu; }, updateSearchColumnsMenu: function () { var columns = this.getCmp().up('grid').getColumns(); Ext.Array.each(columns, function (column) { this.getFilterColumnsMenu().add({ xtype: 'menucheckitem', text: column.text, dataIndex: column.dataIndex, checked: true, listeners: { checkchange: { fn: function (menuCheckItem, checked) { if (checked) { var selectAllColumnsMenuItemCheck = true; this.getFilterColumnsMenu().items.each(function (item) { if (item.dataIndex && item.checked == false) { selectAllColumnsMenuItemCheck = false; return false; } }); this.getSelectAllColumnsMenuItem().setChecked(selectAllColumnsMenuItemCheck, true); } else { this.getSelectAllColumnsMenuItem().setChecked(false, true); } this.doFilter(); }, scope: this } } }); }, this); this.getFilterSplitButton().setMenu(this.getFilterColumnsMenu()); }, doFilter: function () { var searchColumnIndexes = [], searchValue = this.getFilterTextField().getValue();; this.getFilterSplitButton().getMenu().items.each(function (item) { if (item.dataIndex && item.checked) { searchColumnIndexes.push(item.dataIndex); } }, this); if (this.getStore().remoteFilter) { this.remoteFilter(searchColumnIndexes, searchValue) } else { this.localFilter(searchColumnIndexes, searchValue); } }, localFilter: function (searchColumnIndexes, searchValue) { this.getStore().removeFilter(this.filter); this.filter = new Ext.util.Filter({ filterFn: function (record) { if (searchColumnIndexes.length === 0 || Ext.isEmpty(searchValue)) { return true; } var found = false; Ext.Array.each(searchColumnIndexes, function (dataIndex) { if (record.get(dataIndex) && record.get(dataIndex).indexOf(searchValue) != -1) { found = true; return false; } }, this); return found; } }); this.getStore().addFilter(this.filter); }, remoteFilter: function (searchColumnIndexes, searchValue) { var remoteFilters = []; Ext.Array.each(searchColumnIndexes, function (columnIndex) { remoteFilters.push({ property: columnIndex, value: searchValue }); }); this.getStore().clearFilter(); this.getStore().filter(remoteFilters); }, getStore: function () { if (!this.store) { this.store = this.getCmp().up('grid').getStore(); } return this.store; } });