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.