Добавляем Tree в Combobox

В данной статье мы создадим Ext.tree.Panel вместо выпадающего списка (boundlist) в Ext.form.field.ComboBox. Для этого нам придется расширить абстрактный класс Ext.form.field.Picker, в этом примере я попытался реализовать удаленные и локальные фильтра. Так же вы можете определить valueField и displayField — так же как это делается в обычном ComboBox. Что бы включить выбор папок, установите свойство alloFolderSelect в значение true.

Данная доработка состоит из двух классов: панели TreeComboBox и TreeComboBoxList (что то вроде стандартного boundlist у combobox). Не забывайте, что это только примерная реализация и она может содержать ошибки! Не стесняйтесь изменять код в соответствии с вашими требованиями.

Ext.define('app.ux.form.field.TreeComboBoxList', {
    extend: 'Ext.tree.Panel',
    alias: 'widget.treecomboboxlist',

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

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

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

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

    selectFirstLeaf: function () {
        var firstLeaf = this.getStore().findRecord('leaf', true);
        this.getSelectionModel().select(firstLeaf);
    },

    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.displayField).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.displayField,
            value: searchValue
        }));
    },

    onPickerStoreLoad: function (store, records) {
        this.fireEvent('filtered', store, this);
    }
});
Ext.define('app.ux.form.field.TreeComboBox', {
    extend: 'Ext.form.field.Picker',
    requires: [
        'app.ux.form.field.TreeComboBoxList'
    ],

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

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

    treeConfig: {
        // Tree Config
    },

    initComponent: function () {
        this.on('change', this.onTreeComboValueChange, this, {
            buffer: this.filterDelayBuffer
        });
        this.callParent();
    },

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

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

    createPicker: function () {
        var treeConfig = Ext.apply({
            xtype: 'treecomboboxlist',
            id: this.getId() + '-TreePicker',
            store: this.getPickerStore(),
            valueField: this.valueField,
            displayField: this.displayField,
            anyMatch: this.anyMatch,
            allowFolderSelect: this.allowFolderSelect
        }, this.treeConfig);
        var treePanelPicker = Ext.widget(treeConfig);

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

    onFiltered: function (store, treeList) {
        if (store.getCount() > 0) {
            this.expand();
            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;
    }
});

Чтобы изменить конфигурацию TreeComboBoxList при инициализации, вам нужно будет изменить свойство treeConfig. В примере на Fiddle вы можете увидеть два поля (первое для локальной фильтрации, второе для удаленной). Так же обратите внимание на Ext.ux.TreePicker

Fiddle:

Один комментарий

  1. Спасибо, отличный блог. C удовольствием просмотрел все статьи. Не понимаю, почему не наткнулся на него когда писал свою реализацию treecombo )) Не прекращай делиться интересными идеями!

Добавить комментарий

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