В данной статье мы создадим 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:
Спасибо, отличный блог. C удовольствием просмотрел все статьи. Не понимаю, почему не наткнулся на него когда писал свою реализацию treecombo )) Не прекращай делиться интересными идеями!