Добавляем Grid в ComboBox

В данном примере я покажу как заменить Ext.view.BoundList, который используется по умолчанию для отображения выпадающего списка в Ext.form.field.ComboBox, на обычный Ext.grid.Panel. Так же во второй части мы попробуем доработать Ext.view.BoundList , так что бы он выглядел как таблица.

PS:  Поддерживается  и локальная и удаленная фильтрация.

Часть 1 — Используем обычный Grid

Так же я использовал вычисляемое поле fullName в модели, что бы использовать его в качестве значения в combobox. Вы также можете использовать свойство displayTpl для достижения аналогичного результата. Свойство filter (имя поля модели) — используется для фильтрации данных, оно определяется в настройке filterField для Ext.form.field.ComboBox.

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

Fiddle:

Часть 2 — Дорабатываем стандартный boundlist

Если вам не нужен такой сложный компонент как Ext.grid.Panel , вы всегда можете изменить XTemplate у boundlist — так что бы он выглядел как таблица. Конечно, у вас не будет функционала который могла бы дать Ext.grid.Panel  — например selectionModel, фильтрация и пр. Так же понадобиться хорошо сверстать html и css да так, что бы не было кросс-браузерных проблем. В не которых случаях это будет требовать определенного времени, все это вы сможете сделать сами. В этой части я расширил Ext.form.field.ComboBox и Ext.view.BoundList

Ext.define('app.ux.form.field.GridBoundList', {
    extend: 'Ext.view.BoundList',
    alias: 'widget.gridboundlist',

    generateTpl: function () {
        var me = this;

        me.tpl = new Ext.XTemplate(
            '<table style="border-collapse: collapse;">',
                '<thead>',
                    '<tr>' + me.getHeaderTpl() + '</tr>',
                '</thead>',
                '<tbody style="height: 120px; overflow-y: auto;">',
                    '<tpl for=".">',
                        '<tr role="option" unselectable="on" class="x-grid-row ' + me.itemCls + '">' + me.getInnerTpl() + '</tr>',
                    '</tpl>',
                '</tbody>',
            '</table>'
        );
    },

    getHeaderTpl: function() {
        var tpl = [];
        Ext.Array.each(this.columns, function(column) {
            tpl.push('<th class="x-grid-header-ct"><div class="x-column-header-inner">' + column.text + '</div></th>');
        }, this);
        return tpl.join('');
    },

    getInnerTpl: function() {
        var tpl = [];
        Ext.Array.each(this.columns, function(column) {
            tpl.push('<td class="x-grid-cell x-grid-td"><div class="x-grid-cell-inner">{' + column.dataIndex + '}</div></td>');
        }, this);
        return tpl.join('');
    },

    initComponent: function () {
        this.callParent();
    }
});
Ext.define('app.ux.form.field.GridComboBox', {
    extend: 'Ext.form.field.ComboBox',
    alias: ['widget.gridcombobox', 'widget.gridcombo'],
    requires: [
        'Ext.util.DelayedTask',
        'app.ux.form.field.GridBoundList',
        'Ext.data.StoreManager'
    ],

    createPicker: function () {
        var me = this,
            picker,
            pickerCfg = Ext.apply({
                xtype: 'gridboundlist',
                id: me.id + '-gridpicker',
                pickerField: me,
                selectionModel: me.pickerSelectionModel,
                floating: true,
                hidden: true,
                store: me.getPickerStore(),
                displayField: me.displayField,
                preserveScrollOnRefresh: true,
                pageSize: me.pageSize,
                tpl: me.tpl,
                ariaSelectable: me.ariaSelectable,
                columns: me.columns
            }, me.listConfig, me.defaultListConfig);

        picker = me.picker = Ext.widget(pickerCfg);
        if (me.pageSize) {
            picker.pagingToolbar.on('beforechange', me.onPageChange, me);
        }

        // We limit the height of the picker to fit in the space above
        // or below this field unless the picker has its own ideas about that.
        if (!picker.initialConfig.maxHeight) {
            picker.on({
                beforeshow: me.onBeforePickerShow,
                scope: me
            });
        }
        picker.getSelectionModel().on({
            beforeselect: me.onBeforeSelect,
            beforedeselect: me.onBeforeDeselect,
            focuschange: me.onFocusChange,
            scope: me
        });

        picker.getNavigationModel().navigateOnSpace = false;

        return picker;
    },

    initComponent: function () {
        this.callParent();
    }
});

Fiddle:

 

2 комментария

  1. А почему бы не вернуть createPicker, обычный Grid? Тогда можно было бы Grid конфигурировать и не мучиться с XTemplate.

Добавить комментарий для Максим Отменить ответ

Ваш адрес email не будет опубликован. Обязательные поля помечены *