Добавляем 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 комментарий

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

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

Ваш адрес email не будет опубликован.