如果您真正想要的是一种setDisplayField
方法,那么它实际上存在并且被调用setRawValue
。
但是,它本身是否足以解决您的问题是值得怀疑的。确实,您可以使用它来获得正确的显示,但您将失去您的价值......
var legacyData = [
{id: 'd', name: "Doll"}
];
var combo = Ext.widget({
renderTo: Ext.getBody()
,xtype: 'combo'
,fieldLabel: "Regular combo"
,valueField: 'id'
,displayField: 'name'
,queryMode: 'local'
,value: 'd'
,store: {
fields: ['id', 'name']
,data: []
,autoLoad: true
,listeners: {
load: function() {
var value = combo.value,
valueField = combo.valueField,
displayField = combo.displayField,
o = Ext.Array.findBy(legacyData, function(data) {
return data[valueField] === value;
});
if (o) {
combo.setRawValue(o[displayField]);
}
// Fail! This will alert 'Doll' instead of 'd'.
//
// If you set forceSelection to true, the value will be cleared
// all together.
//
alert(combo.getValue());
}
}
}
});
正如 dbring 建议的那样,将历史记录添加到组合的商店确实会解决您的问题,当且仅当您愿意让用户选择历史记录时。据我了解,情况并非如此,因为否则你一开始就不会有任何问题......
在检查了组合类的代码后,我认为实现所需的最佳方法是重写该findRecord
方法。此方法返回的任何记录都将用于更新组合框显示及其值,与此记录是否存在于组合存储中无关,并且不会影响可以在组合中选择的值。完美的!
假设我们在legacyStore
组合中添加了一个,以下是如何覆盖该findRecord
方法来做到这一点:
findRecord: function(field, value) {
var ls = this.legacyStore,
record = this.callParent(arguments);
if (record) {
// found in current store
return record;
} else if (ls) {
// try legacy value store
var idx = ls.findExact(field, value);
return idx !== -1 ? ls.getAt(idx) : false;
} else {
return false;
}
}
现在,我们还有另外几个问题需要处理......
首先,如果findRecord
调用时没有加载legacy store怎么办?那是行不通的。在其 vanilla 形式中,如果未加载其存储,组合框将中止其setValue
方法,并从其方法中回调它onLoad
。我们应该添加一些代码来为我们的旧商店复制这种行为。
第二个问题比较琐碎,但是如果使用历史值初始化组合,并且用户更改组合的值但随后后悔他们的选择怎么办?如果不取消并重新填写整个表单,他们将无法取回原始值。为了避免他们感到沮丧,我们可以更改组合行为以在清除时恢复其原始值,而不是其最后一个值。由于我不知道这是否可取,因此我们将其添加为选项。
考虑到这一切,这里有一个“ux”的完整代码,它将处理您的特定用例。
请注意,它包括一个全局修复程序,该错误会杀死组合框初始值,如果forceSelection
设置为true
并且商店是使用autoLoad: true
. 有关此问题的更多信息。或者,您可以跳过全局修复并添加loading: true
到使用forceSelection: true
.
/**
* Fixes the bug with autoLoad: true and forceSelection: true.
*
* See: https://stackoverflow.com/a/14440479/1387519
*/
Ext.define('Ext.ux.fix.data.Store.AutoLoadLoadingState', {
override: 'Ext.data.Store'
,constructor: function() {
this.callParent(arguments);
if (this.autoLoad) {
this.loading = true;
}
}
});
/**
* A combo box with support for legacy values.
*
* Legacy values will be displayed if their value is assigned to the
* combo, but they won't be selectable (nor queryable) by the user.
*/
Ext.define('Ext.ux.form.field.LegacyValueCombo', {
extend: 'Ext.form.field.ComboBox'
,alias: 'widget.legacyvaluecombo'
,requires: [
'Ext.ux.fix.data.Store.AutoLoadLoadingState'
]
/**
* The store that provides legacy values.
*
* @cfg {Ext.data.Store/Object/String}
*/
,legacyStore: undefined
/**
* If `true` and {@link #forceSelection} is also `true`, the
* {@link #originalValue original value} of the field will be restored when
* the field is cleared, instead of the last value.
*
* This may be useful considering that if the combo original value is a
* legacy one, and the user changes this value, they will have no other
* means to get it back afterward.
*
* @cfg {Boolean}
*/
,restoreOriginalValue: true
/**
* Ensure the legacy store is a store instance before initializing the
* combo box.
*/
,initComponent: function() {
var legacyStore = this.legacyStore;
if (legacyStore) {
this.legacyStore = Ext.data.StoreManager.lookup(legacyStore);
}
this.callParent(arguments);
}
/**
* Overridden to return a legacy record if the value is not found in the
* regular store.
*
* This method will only work if both stores have been loaded.
*/
,findRecord: function(field, value) {
var ls = this.legacyStore,
record = this.callParent(arguments);
if (record) {
// found in current store
return record;
} else if (ls) {
// try legacy value store
var idx = ls.findExact(field, value);
return idx !== -1 ? ls.getAt(idx) : false;
} else {
return false;
}
}
/**
* This method is overridden to support the case where the legacy store
* loads after the regular store.
*/
,setValue: function(value) {
var store = this.store,
legacyStore = this.legacyStore;
if (legacyStore) {
// If the legacy store has not been loaded, trigger it.
if (!legacyStore.lastOptions) {
if (!legacyStore.loading) {
legacyStore.load();
}
}
// Legacy store is loading, we must wait for it to be done before
// processing the value.
if (legacyStore.loading) {
// This is done in the parent setValue method, but we are not
// sure it will be called in time so we must replicate this
// code here.
this.value = value;
this.setHiddenValue(value);
// If the regular store is loading, then this method will be
// called again when it is done. We will see at this time if we
// still have to wait for the legacy store. In the other case,
// we have to simulate a call to onLoad when the legacy store
// has loaded.
if (!store.loading) {
legacyStore.on({
single: true
,scope: this
,load: function(legacyStore, records, success) {
this.onLoad(store, store.getRange(), success);
}
});
}
}
// Legacy store is loaded, that's OK to continue.
else {
this.callParent(arguments);
// Implements restoreOriginalValue.
if (this.restoreOriginalValue) {
this.lastSelection = this.originalValue;
}
}
// setValue must return this
return this;
}
// No legacy store, just defer to the super method.
else {
return this.callParent(arguments);
}
}
});
最后,这里有一个现场演示,其中包含大量示例,证明此 ux 可以与同步和异步存储的所有组合一起使用,而forceSelection
......虽然setRawValue
不能。