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);
    }

});