I have already shown how to implement a Tree ComboBox. In the following example you can see how to implement a simpler ComboBox with a grid picker. It is implemented with support of the local and remote filtering. I have used also a calculated model field ‘fullName’ to use it as display value in the combo. You can also use as ‘displayTpl’┬áproperty to achieve the same effect.

The filter property, the model field name which is used to filter data is defined in ‘filterField’ setting of theComboBox.

Ext.define('app.ux.form.field.GridComboBox', {
    extend: 'Ext.form.field.Picker',
    requires: [
        'app.ux.form.field.GridComboBoxList'
    ],

    store: false,
    queryMode: 'local',
    anyMatch: false,

    filterDelayBuffer: 300,
    enableKeyEvents: true,
    valueField: 'text',
    selectedRecord: false,

    gridConfig: {
        // Grid Config
    },

    initComponent: function () {
        this.on('change', this.onGridComboValueChange, this, {
            buffer: this.filterDelayBuffer
        });
        this.on('keydown', this.onItemKeyDown, this, {
            buffer: this.filterDelayBuffer
        });

        this.callParent();
    },

    onGridComboValueChange: function (field, value) {
        this.selectedRecord = false;
        switch (this.queryMode) {
        case 'local':
            this.getPicker().doLocalQuery(value)
            break;
        case 'remote':
            this.getPicker().doRemoteQuery(value);
            break;
        }
    },

    onItemKeyDown: function() {
        this.expand();
    },

    expand: function () {
        this.callParent([arguments]);
    },

    createPicker: function () {
        var gridConfig = Ext.apply({
            xtype: 'gridcomboboxlist',
            id: this.getId() + '-GridPicker',
            store: this.getPickerStore(),
            valueField: this.valueField,
            displayField: this.displayField,
            anyMatch: this.anyMatch,
            allowFolderSelect: this.allowFolderSelect,
            columns: this.columns,
            filterField: this.filterField
        }, this.gridConfig);
        var gridPanelPicker = Ext.widget(gridConfig);

        gridPanelPicker.on({
            picked: this.onPicked,
            filtered: this.onFiltered,
            beforeselect: this.onBeforeSelect,
            beforedeselect: this.onBeforeDeselect,
            scope: this
        });
        return gridPanelPicker;
    },

    onFiltered: function (store, gridList) {
        if (store.getCount() > 0) {
            this.focus();
        }
    },

    getPickerStore: function () {
        return this.store;
    },

    onPicked: function (record) {
        this.suspendEvent('change');
        this.selectedRecord = record;
        this.setValue(record.get(this.displayField));
        this.collapse();
        this.resumeEvent('change');
        this.fireEvent('select', record);
    },

    getValue: function () {
        var value;
        if (this.valueField && this.selectedRecord) {
            value = this.selectedRecord.get(this.valueField);
        } else {
            value = this.getRawValue();
        }
        return value;
    },

    getSubmitValue: function () {
        var value = this.getValue();
        if (Ext.isEmpty(value)) {
            value = '';
        }
        return value;
    },
    onBeforeSelect: function (comboBox, record, recordIndex) {
        return this.fireEvent('beforeselect', this, record, recordIndex);
    },

    onBeforeDeselect: function (comboBox, record, recordIndex) {
        return this.fireEvent('beforedeselect', this, record, recordIndex);
    },

    getSelectedRecord: function () {
        return this.selectedRecord;
    }
});

 

Ext.define('app.ux.form.field.GridComboBoxList', {
    extend: 'Ext.grid.Panel',
    alias: 'widget.gridcomboboxlist',

    floating: true,
    hidden: true,
    value: false,
    anyMatch: false,

    initComponent: function () {
        this.listeners = {
            'cellclick': this.onCellClick,
            'itemkeydown': this.onItemKeyDown
        };
        this.callParent();
    },

    onCellClick: function (tree, td, cellIndex, record, tr, rowIndex, e, eOpts) {
        this.fireEvent('picked', record);
    },

    onItemKeyDown: function (view, record, item, index, e, eOpts) {
        if (e.keyCode == e.ENTER) {
            this.fireEvent('picked', record);
        }
    },

    selectFirstRow: function () {
        var firstRecord = this.getStore().getAt(0);
        this.getSelectionModel().select(firstRecord);
    },

    doLocalQuery: function (searchValue) {
        var store = this.getStore();
        this.searchValue = searchValue.toLowerCase();

        store.setRemoteFilter(false);
        store.filterBy(this.pickerStoreFilter, this);
        this.fireEvent('filtered', store, this);
    },

    pickerStoreFilter: function (record) {
        var itemValue = record.get(this.filterField).toLowerCase();
        if (this.anyMatch) {
            if (itemValue.indexOf(this.searchValue) != -1) {
                return true;
            }
        } else {
            if (itemValue.startsWith(this.searchValue)) {
                return true;
            }
        }
        return false;
    },

    doRemoteQuery: function (searchValue) {
        var store = this.getStore();
        store.setRemoteFilter(true);
        store.on('load', this.onPickerStoreLoad, this, {
            single: true
        });
        store.filter(new Ext.util.Filter({
            anyMatch: this.anyMatch,
            disableOnEmpty: true,
            property: this.filterField,
            value: searchValue
        }));
    },

    onPickerStoreLoad: function (store, records) {
        this.fireEvent('filtered', store, this);
    }
});

In the following fiddle I have used the ‘app.view.cmp.employees.ComboBox’┬áclass to not define the same column model for two remote and local filtering comboboxes.