В данной статье мы создадим 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 )) Не прекращай делиться интересными идеями!