Filter paging toolbar will help users to filter the rows of grid. Users can select as target all or some of columns. It works with remote and local filtering. The code is pushed into one class (extension of the paging toolbar) to make the usage easier.
Configuration peropties:
filterItemWidth: 300,
filterOnType: true,
filterSplitButtonGlyph: ‘xf0b0@FontAwesome’,
filterSplitButtonText: “Filter”
Do not forget to set the ‘remoteFilter’ property of the store if you will need to filter all the data, not only the current page content. The filter paging toolbar works for Ext JS 6.5.3 – Classic Toolkit.
Ext.define('PagingToolbarWithSearch', { extend: 'Ext.toolbar.Paging', xtype: 'pagingtoolbarwithsearch', filterItemWidth: 300, filterOnType: true, filterSplitButtonGlyph: 'xf0b0@FontAwesome', filterSplitButtonText: "Filter", getPagingItems: function () { var me = this, inputListeners = { scope: me, blur: me.onPagingBlur }; inputListeners[Ext.supports.SpecialKeyDownRepeat ? 'keydown' : 'keypress'] = me.onPagingKeyDown; return [{ itemId: 'first', tooltip: me.firstText, overflowText: me.firstText, iconCls: Ext.baseCSSPrefix + 'tbar-page-first', disabled: true, handler: me.moveFirst, scope: me }, { itemId: 'prev', tooltip: me.prevText, overflowText: me.prevText, iconCls: Ext.baseCSSPrefix + 'tbar-page-prev', disabled: true, handler: me.movePrevious, scope: me }, '-', me.beforePageText, { xtype: 'numberfield', itemId: 'inputItem', name: 'inputItem', cls: Ext.baseCSSPrefix + 'tbar-page-number', allowDecimals: false, minValue: 1, hideTrigger: true, enableKeyEvents: true, keyNavEnabled: false, selectOnFocus: true, submitValue: false, // mark it as not a field so the form will not catch it when getting fields isFormField: false, width: me.inputItemWidth, margin: '-1 2 3 2', listeners: inputListeners }, { xtype: 'tbtext', itemId: 'afterTextItem', html: Ext.String.format(me.afterPageText, 1) }, '-', { itemId: 'next', tooltip: me.nextText, overflowText: me.nextText, iconCls: Ext.baseCSSPrefix + 'tbar-page-next', disabled: true, handler: me.moveNext, scope: me }, { itemId: 'last', tooltip: me.lastText, overflowText: me.lastText, iconCls: Ext.baseCSSPrefix + 'tbar-page-last', disabled: true, handler: me.moveLast, scope: me }, '-', { itemId: 'refresh', tooltip: me.refreshText, overflowText: me.refreshText, iconCls: Ext.baseCSSPrefix + 'tbar-loading', disabled: me.store.isLoading(), handler: me.doRefresh, scope: me }, '-', this.getFilterSplitButton(), this.getFilterTextField() ]; }, 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; }, beforeRender: function () { this.callParent(arguments); this.updateBarInfo(); this.updateSearchColumnsMenu(); }, 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.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.store.remoteFilter) { this.remoteFilter(searchColumnIndexes, searchValue) } else { this.localFilter(searchColumnIndexes, searchValue); } }, localFilter: function (searchColumnIndexes, searchValue) { this.store.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.store.addFilter(this.filter); }, remoteFilter: function (searchColumnIndexes, searchValue) { var remoteFilters = []; Ext.Array.each(searchColumnIndexes, function (columnIndex) { remoteFilters.push({ property: columnIndex, value: searchValue }); }); this.store.clearFilter(); this.store.filter(remoteFilters); } });