Javascript ExtJS 4.2-Tab在组合框中选择了错误的值,键入Fast时使用typeAhead:true
当在ExtJS中使用组合框(在4.2中测试过,但也可能在其他版本中测试过)并设置了“typeAhead:true”选项时,如果用户非常快地键入值,然后点击键盘上的“tab”,焦点将移到屏幕上的下一个字段,并且设置了错误的值。由于这个bug的棘手性质,我在这里为它创建了一个JSFIDLE: 要复制该bug,请在第一个组合框字段中快速键入“975”,然后键入“tab”。如果在“975”中输入“5”后很快点击tab,您将看到combobox被设置为“970”选项。我相信这是因为“Tab”导致列表中突出显示的任何选项都是已设置的值,但奇怪的是,“970”在“975”中输入“5”后仍然突出显示,此时它应该在“Tab”之前首先处理该事件,并且它应该将选择更改为正确的“975” 我尝试过调整typeAheadDelay(在示例中设置为0),以及QueryDisplay和我能想到的所有其他内容。看起来ExtJS只是简单地取消了在按下选项卡时仍在运行且未完成的查找 有没有关于如何解决这个bug的建议?我是否需要编写自己的“typeAhead”自动完成函数,通过单线程处理事件来正确处理此问题 下面是示例JSFIDLE代码,显示了以下内容:Javascript ExtJS 4.2-Tab在组合框中选择了错误的值,键入Fast时使用typeAhead:true,javascript,extjs,extjs4,Javascript,Extjs,Extjs4,当在ExtJS中使用组合框(在4.2中测试过,但也可能在其他版本中测试过)并设置了“typeAhead:true”选项时,如果用户非常快地键入值,然后点击键盘上的“tab”,焦点将移到屏幕上的下一个字段,并且设置了错误的值。由于这个bug的棘手性质,我在这里为它创建了一个JSFIDLE: 要复制该bug,请在第一个组合框字段中快速键入“975”,然后键入“tab”。如果在“975”中输入“5”后很快点击tab,您将看到combobox被设置为“970”选项。我相信这是因为“Tab”导致列表中突出
// The data store containing the list of values
var states = Ext.create('Ext.data.Store', {
fields: ['val_number', 'val_name'],
data : [
{"val_number":"970", "val_name":"970 - Name"},
{"val_number":"971", "val_name":"971 - Name"},
{"val_number":"972", "val_name":"972 - Name"},
{"val_number":"973", "val_name":"973 - Name"},
{"val_number":"974", "val_name":"974 - Name"},
{"val_number":"975", "val_name":"975 - Name"}
//...
]
});
Ext.create('Ext.form.ComboBox', {
fieldLabel: 'Choose 1st Value',
store: states,
queryMode: 'local',
displayField: 'val_name',
valueField: 'val_number',
renderTo: Ext.getBody(),
typeAhead: true,
typeAheadDelay: 0,
minChars: 1,
forceSelection: true,
autoSelect: false,
triggerAction: 'all',
queryDelay: 0,
queryCaching: false
});
Ext.create('Ext.form.ComboBox', {
fieldLabel: 'Choose 2nd Value',
store: states,
queryMode: 'local',
displayField: 'val_name',
valueField: 'val_number',
renderTo: Ext.getBody(),
typeAhead: true,
typeAheadDelay: 0,
minChars: 1,
forceSelection: true,
autoSelect: false,
triggerAction: 'all',
queryDelay: 0,
queryCaching: false
});
更新:
按建议尝试此代码,结果没有更改-仍然无法正确选择:
Ext.define('App.CustomComboBox', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.CustomCombobox',
initComponent:function() {
// call parent init component
this.callParent(arguments);
},
onTypeAhead: function() {
console.log('onTypeAhead...');
var me = this,
displayField = me.displayField,
record = me.store.findRecord(displayField, me.getRawValue()),
boundList = me.getPicker(),
newValue, len, selStart;
if (record) {
newValue = record.get(displayField);
len = newValue.length;
selStart = me.getRawValue().length;
//boundList.highlightItem(boundList.getNode(record));
if (selStart !== 0 && selStart !== len) {
me.setRawValue(newValue);
me.selectText(selStart, newValue.length);
}
}
}
});
发现问题:
问题不在于typeAhead
的selectOnTab
与autoSelect
一起,后者将从typeAhead
设置为true
所以发生了这样的情况:
- 无法为任务提供回调,因此在查询完成后调用
:(assertValue
- 您需要再次禁用autoselect,我找到的唯一方法是覆盖
,并注释掉突出显示:onTypeAhead
多亏了Jandalf,我有了一些好消息。我能够通过扩展combobox并引入一些修复程序来解决我的需要。第一个是按照Jandalf的建议做(一个很好的起点),下一组修复程序是停止使用DelayedTask,如果延迟为0或更小(我的配置设置为“typeAheadDelay”和“queryDelay”)最后,我还必须检查“assertValue”这相当于当有人键入一个常规键来捕捉在键控完成之前标签模糊的问题时所发生的情况。由于最后一部分的原因,它可能不是每个人的完美解决方案,但它是唯一可以解决我的问题的方法。因此,下面的代码使它适用于我。我希望有人能够se会发现它很有用
Ext.define('App.CustomComboBox', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.CustomCombobox',
initComponent:function() {
// call parent init component
this.callParent(arguments);
},
onTypeAhead: function() {
var me = this,
displayField = me.displayField,
record = me.store.findRecord(displayField, me.getRawValue()),
boundList = me.getPicker(),
newValue, len, selStart;
if (record) {
newValue = record.get(displayField);
len = newValue.length;
selStart = me.getRawValue().length;
//Removed to prevent onBlur/Tab causing invalid selections
//boundList.highlightItem(boundList.getNode(record));
if (selStart !== 0 && selStart !== len) {
me.setRawValue(newValue);
me.selectText(selStart, newValue.length);
}
}
},
onPaste: function(){
var me = this;
if (!me.readOnly && !me.disabled && me.editable) {
if (me.queryDelay > 0) {
//Delay it
me.doQueryTask.delay(me.queryDelay);
} else {
//Changed to do immediately instead of in the delayed task
me.doRawQuery();
}
}
},
// store the last key and doQuery if relevant
onKeyUp: function(e, t) {
var me = this,
key = e.getKey();
if (!me.readOnly && !me.disabled && me.editable) {
me.lastKey = key;
// we put this in a task so that we can cancel it if a user is
// in and out before the queryDelay elapses
// perform query w/ any normal key or backspace or delete
if (!e.isSpecialKey() || key == e.BACKSPACE || key == e.DELETE) {
if (me.queryDelay > 0) {
//Delay it
me.doQueryTask.delay(me.queryDelay);
} else {
//Changed to do immediately instead of in the delayed task
me.doRawQuery();
}
}
}
if (me.enableKeyEvents) {
me.callParent(arguments);
}
},
// private
assertValue: function() {
var me = this,
value = me.getRawValue(),
rec, currentValue;
if (me.forceSelection) {
if (me.multiSelect) {
// For multiselect, check that the current displayed value matches the current
// selection, if it does not then revert to the most recent selection.
if (value !== me.getDisplayValue()) {
me.setValue(me.lastSelection);
}
} else {
// For single-select, match the displayed value to a record and select it,
// if it does not match a record then revert to the most recent selection.
rec = me.findRecordByDisplay(value);
if (rec) {
currentValue = me.value;
// Prevent an issue where we have duplicate display values with
// different underlying values.
if (!me.findRecordByValue(currentValue)) {
me.select(rec, true);
}
} else {
//Try and query the value to find it as a "catch" for the blur happening before the last keyed value was entered
me.doRawQuery();
//Get the new value to use
value = me.getRawValue();
//Copy of the above/same assert value check
rec = me.findRecordByDisplay(value);
if (rec) {
currentValue = me.value;
// Prevent an issue where we have duplicate display values with
// different underlying values.
if (!me.findRecordByValue(currentValue)) {
me.select(rec, true);
}
} else {
//This is the original "else" condition
me.setValue(me.lastSelection);
}
}
}
}
me.collapse();
},
doTypeAhead: function() {
var me = this;
if (!me.typeAheadTask) {
me.typeAheadTask = new Ext.util.DelayedTask(me.onTypeAhead, me);
}
if (me.lastKey != Ext.EventObject.BACKSPACE && me.lastKey != Ext.EventObject.DELETE) {
//Changed to not use the delayed task if 0 or less
if (me.typeAheadDelay > 0) {
me.typeAheadTask.delay(me.typeAheadDelay);
} else {
me.onTypeAhead();
}
}
}
});
我只是尝试了这一点,只是扩展并更改以注释掉那一行,但问题仍然存在。它没有突出显示,但仍在按照您所说的取消查询,因此它没有得到正确的值。还有其他想法吗?我已经用我根据您的建议尝试的代码更新了我的问题。是的,这是正确的,但您没有机会执行ute查询,选择值,然后模糊字段,因此我认为这比之前更好。我真的很感谢您花时间来做这件事,但看起来我需要做我自己版本的typeAhead,因为不幸的是,这不是一个完整的解决方案。我正在考虑阻止整个“onBlur”然后切换到监听tab/enter/etc.onkeyup。如果我找到完整的工作解决方案,我会在这里更新。
onTypeAhead: function() {
var me = this,
displayField = me.displayField,
record = me.store.findRecord(displayField, me.getRawValue()),
boundList = me.getPicker(),
newValue, len, selStart;
if (record) {
newValue = record.get(displayField);
len = newValue.length;
selStart = me.getRawValue().length;
//boundList.highlightItem(boundList.getNode(record));
if (selStart !== 0 && selStart !== len) {
me.setRawValue(newValue);
me.selectText(selStart, newValue.length);
}
}
}
Ext.define('App.CustomComboBox', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.CustomCombobox',
initComponent:function() {
// call parent init component
this.callParent(arguments);
},
onTypeAhead: function() {
var me = this,
displayField = me.displayField,
record = me.store.findRecord(displayField, me.getRawValue()),
boundList = me.getPicker(),
newValue, len, selStart;
if (record) {
newValue = record.get(displayField);
len = newValue.length;
selStart = me.getRawValue().length;
//Removed to prevent onBlur/Tab causing invalid selections
//boundList.highlightItem(boundList.getNode(record));
if (selStart !== 0 && selStart !== len) {
me.setRawValue(newValue);
me.selectText(selStart, newValue.length);
}
}
},
onPaste: function(){
var me = this;
if (!me.readOnly && !me.disabled && me.editable) {
if (me.queryDelay > 0) {
//Delay it
me.doQueryTask.delay(me.queryDelay);
} else {
//Changed to do immediately instead of in the delayed task
me.doRawQuery();
}
}
},
// store the last key and doQuery if relevant
onKeyUp: function(e, t) {
var me = this,
key = e.getKey();
if (!me.readOnly && !me.disabled && me.editable) {
me.lastKey = key;
// we put this in a task so that we can cancel it if a user is
// in and out before the queryDelay elapses
// perform query w/ any normal key or backspace or delete
if (!e.isSpecialKey() || key == e.BACKSPACE || key == e.DELETE) {
if (me.queryDelay > 0) {
//Delay it
me.doQueryTask.delay(me.queryDelay);
} else {
//Changed to do immediately instead of in the delayed task
me.doRawQuery();
}
}
}
if (me.enableKeyEvents) {
me.callParent(arguments);
}
},
// private
assertValue: function() {
var me = this,
value = me.getRawValue(),
rec, currentValue;
if (me.forceSelection) {
if (me.multiSelect) {
// For multiselect, check that the current displayed value matches the current
// selection, if it does not then revert to the most recent selection.
if (value !== me.getDisplayValue()) {
me.setValue(me.lastSelection);
}
} else {
// For single-select, match the displayed value to a record and select it,
// if it does not match a record then revert to the most recent selection.
rec = me.findRecordByDisplay(value);
if (rec) {
currentValue = me.value;
// Prevent an issue where we have duplicate display values with
// different underlying values.
if (!me.findRecordByValue(currentValue)) {
me.select(rec, true);
}
} else {
//Try and query the value to find it as a "catch" for the blur happening before the last keyed value was entered
me.doRawQuery();
//Get the new value to use
value = me.getRawValue();
//Copy of the above/same assert value check
rec = me.findRecordByDisplay(value);
if (rec) {
currentValue = me.value;
// Prevent an issue where we have duplicate display values with
// different underlying values.
if (!me.findRecordByValue(currentValue)) {
me.select(rec, true);
}
} else {
//This is the original "else" condition
me.setValue(me.lastSelection);
}
}
}
}
me.collapse();
},
doTypeAhead: function() {
var me = this;
if (!me.typeAheadTask) {
me.typeAheadTask = new Ext.util.DelayedTask(me.onTypeAhead, me);
}
if (me.lastKey != Ext.EventObject.BACKSPACE && me.lastKey != Ext.EventObject.DELETE) {
//Changed to not use the delayed task if 0 or less
if (me.typeAheadDelay > 0) {
me.typeAheadTask.delay(me.typeAheadDelay);
} else {
me.onTypeAhead();
}
}
}
});