Ext6migrate: Update our ComboGrid component to work with ExtJS6

The ComboGrid combonent requires row-like selection something that
the default ExtJS Ext.selection.DataViewModel used for ComboBox cannot do.

This requires overriding the protected method onBindStore() where the selection
model is set.
This commit is contained in:
Emmanuel Kasper 2016-02-15 09:22:58 +01:00 committed by Dietmar Maurer
parent 5fc9bddb07
commit a263665b16

View File

@ -5,111 +5,228 @@
* https://www.sencha.com/forum/showthread.php?299909
*
*/
Ext.define('PVE.form.ComboGrid', {
extend: 'Ext.form.field.Picker',
extend: 'Ext.form.field.ComboBox',
alias: ['widget.PVE.form.ComboGrid'],
// this value is used as default value after load()
preferredValue: undefined,
// If set to `true`, allows the combo field to hold more than one
// value at a time, and allows selecting multiple items from the
// dropdown list.
multiSelect: false,
defaultPickerConfig: {
maxHeight: 300,
width: 400,
scrollable: true,
floating: true,
// hack: allow to select empty value
// seems extjs does not allow that when 'editable == false'
onKeyUp: function(e, t) {
var me = this;
var key = e.getKey();
if (!me.editable && me.allowBlank && !me.multiSelect &&
(key == e.BACKSPACE || key == e.DELETE)) {
me.setValue('');
}
me.callParent(arguments);
},
displayField: false,
valueField: false,
matchFieldWidth: false,
// if we have value(s) in the textField, mark them as selected in the picker
// private
syncSelection: function() {
var me = this, previousItems = [];
// override ExtJS protected method
onBindStore: function(store, initial) {
var me = this,
picker = me.picker,
extraKeySpec,
valueCollectionConfig;
if (me.getRawValue()) {
Ext.Array.each(me.getRawValue().split(','), function(record) {
var previousItem = me.store.findRecord(me.valueField, record);
// select only what can be found in the ComboGrid store
previousItem != null && previousItems.push(previousItem);
// We're being bound, not unbound...
if (store) {
// If store was created from a 2 dimensional array with generated field names 'field1' and 'field2'
if (store.autoCreated) {
me.queryMode = 'local';
me.valueField = me.displayField = 'field1';
if (!store.expanded) {
me.displayField = 'field2';
}
// displayTpl config will need regenerating with the autogenerated displayField name 'field1'
me.setDisplayTpl(null);
}
if (!Ext.isDefined(me.valueField)) {
me.valueField = me.displayField;
}
// Add a byValue index to the store so that we can efficiently look up records by the value field
// when setValue passes string value(s).
// The two indices (Ext.util.CollectionKeys) are configured unique: false, so that if duplicate keys
// are found, they are all returned by the get call.
// This is so that findByText and findByValue are able to return the *FIRST* matching value. By default,
// if unique is true, CollectionKey keeps the *last* matching value.
extraKeySpec = {
byValue: {
rootProperty: 'data',
unique: false
}
};
extraKeySpec.byValue.property = me.valueField;
store.setExtraKeys(extraKeySpec);
if (me.displayField === me.valueField) {
store.byText = store.byValue;
} else {
extraKeySpec.byText = {
rootProperty: 'data',
unique: false
};
extraKeySpec.byText.property = me.displayField;
store.setExtraKeys(extraKeySpec);
}
// We hold a collection of the values which have been selected, keyed by this field's valueField.
// This collection also functions as the selected items collection for the BoundList's selection model
valueCollectionConfig = {
rootProperty: 'data',
extraKeys: {
byInternalId: {
property: 'internalId'
},
byValue: {
property: me.valueField,
rootProperty: 'data'
}
},
// Whenever this collection is changed by anyone, whether by this field adding to it,
// or the BoundList operating, we must refresh our value.
listeners: {
beginupdate: me.onValueCollectionBeginUpdate,
endupdate: me.onValueCollectionEndUpdate,
scope: me
}
};
// This becomes our collection of selected records for the Field.
me.valueCollection = new Ext.util.Collection(valueCollectionConfig);
// We use the selected Collection as our value collection and the basis
// for rendering the tag list.
//pve override: since the picker is represented by a grid panel,
// we changed here the selection to RowModel
me.pickerSelectionModel = new Ext.selection.RowModel({
mode: me.multiSelect ? 'SIMPLE' : 'SINGLE',
// There are situations when a row is selected on mousedown but then the mouse is dragged to another row
// and released. In these situations, the event target for the click event won't be the row where the mouse
// was released but the boundview. The view will then determine that it should fire a container click, and
// the DataViewModel will then deselect all prior selections. Setting `deselectOnContainerClick` here will
// prevent the model from deselecting.
deselectOnContainerClick: false,
enableInitialSelection: false,
pruneRemoved: false,
selected: me.valueCollection,
store: store,
listeners: {
scope: me,
lastselectedchanged: me.updateBindSelection
}
});
me.picker.getSelectionModel().select(previousItems);
if (!initial) {
me.resetToDefault();
}
if (picker) {
picker.setSelectionModel(me.pickerSelectionModel);
if (picker.getStore() !== store) {
picker.bindStore(store);
}
}
}
},
// copied from ComboBox
createPicker: function() {
var me = this;
var config = Ext.applyIf({
store: me.getStore(),
selModel: {
selType: 'checkboxmodel',
mode: me.multiSelect ? 'SIMPLE' : 'SINGLE',
showHeaderCheckbox: false // shows a selectAll checkbox, not reliable
},
listeners: {
selectionchange: {
fn: function(grid, selectedRecords) {
me.setRecords(selectedRecords);
me.fireEvent('select', me, selectedRecords);
},
scope: me
},
show: {
fn: function() {
me.syncSelection();
},
scope: me
var me = this;
var picker;
var pickerCfg = Ext.apply({
// pve overrides: display a grid for selection
xtype: 'gridpanel',
id: me.pickerId,
pickerField: me,
floating: true,
hidden: true,
store: me.store,
displayField: me.displayField,
preserveScrollOnRefresh: true,
pageSize: me.pageSize,
tpl: me.tpl,
selModel: me.pickerSelectionModel,
focusOnToFront: false
}, me.listConfig, me.defaultListConfig);
picker = me.picker || Ext.widget(pickerCfg);
if (picker.getStore() !== me.store) {
picker.bindStore(store);
}
if (me.pageSize) {
picker.pagingToolbar.on('beforechange', me.onPageChange, me);
}
// pve overrides: pass missing method in gridPanel to its view
picker.refresh = function() {
picker.getSelectionModel().select(me.valueCollection.getRange());
picker.getView().refresh();
};
picker.getNodeByRecord = function() {
picker.getView().getNodeByRecord(arguments);
};
// 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,
selectionChange: function (sm, selectedRecords) {
var me = this;
if (selectedRecords.length) {
me.setValue(selectedRecords);
me.fireEvent('select', me, selectedRecords);
}
}
}, me.defaultPickerConfig);
},
scope: me
});
Ext.apply(config, me.listConfig);
var picker = me.picker = Ext.create('Ext.grid.Panel', config);
picker.getNavigationModel().navigateOnSpace = false;
return picker;
},
setRecords: function(records) {
if (records && !Ext.isArray(records)) {
records = [records];
}
this.selectedRecords = records;
var rawValue = [];
Ext.Array.each(records, function(record) {
rawValue.push(record.get(this.displayField));
}, this);
this.setValue(rawValue);
},
getRecords: function() {
return this.selectedRecords;
},
beforeReset: function() {
if(this.picker) {
this.picker.getSelectionModel().deselectAll()
}
this.callParent(arguments);
},
getStore: function() {
if (!this.store) {
this.store = Ext.create('Ext.data.Store', {});
}
return this.store;
},
initComponent: function() {
var me = this;
me.callParent(arguments);
if (me.initialConfig.editable === undefined) {
me.editable = false;
}
Ext.apply(me, {
queryMode: 'local',
matchFieldWidth: false
});
Ext.applyIf(me, { value: ''}); // hack: avoid ExtJS validate() bug
Ext.applyIf(me.listConfig, { width: 400 });
me.callParent();
// Create the picker at an early stage, so it is available to store the previous selection
if (!me.picker) {
me.createPicker();
}
me.store.on('beforeload', function() {
if (!me.isDisabled()) {