PDA

View Full Version : Can't clear Combobox input field



friend
4 Jan 2012, 9:46 AM
Using ExtJs 4.0.7, I have an Ext.form.Panel which contains several fields, one of which is a Ext.form.field.Combobox. When the form is loaded, the Combobox is assigned a value and the associated display value is shown correctly. However, if you try to clear the input field of the Combobox, then tab to the next field, it consistently restores the initially populated value.

There appears to be no way to clear the combo's input field, even though allowBlank: true is set. Note that I also have forceSelection: false set and tried setting selectOnTab: false as well, with no luck. Even setting emptyText: 'This is an optional field' is no help.

If I load the form using empty values, everything is great; you can repeatedly select an item from the combo's drop-down, then later clear the combo's input field with no problem (even after tabbing back and forth through several other fields).

Here's the Combobox config:



},{
xtype: 'combo',
allowBlank: true,
anchor: '70%',
displayField: 'label',
fieldLabel: 'Location',
forceSelection: false,
name: 'locationId',
queryMode: 'remote',
//selectOnTab: false,
store: 'LabelList',
typeAhead: true,
valueField: 'labelId'
},{


Any constructive suggestions?

mitchellsimoens
4 Jan 2012, 10:39 AM
I'm not seeing this. I can clear out the text and tab to the next field and the value of the combo is empty.

friend
4 Jan 2012, 11:29 AM
The problem seems to be that my store gets auto-instantiated by the MVC framework correctly, but not loaded (even through I have autoLoad: true declared in the store's configuration).

If I call Ext.data.StoreManager.lookup('LabelList').load() in the form panel's afterrender event, everything works fine.

Note the autoLoad attribute in my store's config; shouldn't this cause the store to auto-load after it's instantiated?



Ext.define('App.store.LabelList', {
extend: 'Ext.data.Store',
model: 'App.model.Label',
autoLoad: true,
proxy: {
type: 'ajax',
url: 'label/listAll.action',
reader: {
type: 'json',
root: 'data'
}
},
storeId: 'LabelList'
});

skirtle
4 Jan 2012, 2:39 PM
I can't explain the behaviour you're seeing but I am a little confused by your configuration.

These three options sum up the desired behaviour:


forceSelection: false,
queryMode: 'remote',
typeAhead: true,

That's your classic auto-complete. So far so good. The only thing to note at this point is that minChars defaults to 4 in this configuration, something that often catches people out.

Then I get confused.

Why would you want to auto load a store in this scenario? The loading won't take into account the relevant combobox filtering, so the list of options will be incorrect? Further, as minChars is 4, the combobox won't attempt to load the store manually until 4 characters are typed.

My next source of confusion is this:


displayField: 'label',
valueField: 'labelId'

I don't understand how having differing fields makes sense for comboboxes that don't force the user to choose one of the values.

friend
5 Jan 2012, 4:48 AM
I normally set forceSelection: true, the false setting was just an artifact of testing. So typically, a user in the target app is forced to select from the combo's dropdown list. In this case, each String display 'label' is associated with a primary key ('labelId'), type of Long.

Again, as per my original post, I'm loading a form Panel with existing data, where a user is editing an existing record from a table. In this scenario, you'll note that the combo has a 'name' attribute, so it's getting preloaded with the primary key of the last selected 'label'. Since the store isn't auto-loaded, there are no models/records in the store, hence nothing is displayed in the combo.

Does that make sense?

skirtle
5 Jan 2012, 5:05 AM
I almost understand. I think this might be about to get painful but I have one more question first...

What values are you expecting the autoLoad to load? I still don't understand where that fits in. You're using remote filtering, which implies you have too much data to load it all... so how does it help to load any at all without knowing what the value will be?

friend
5 Jan 2012, 6:21 AM
Understandably, the auto load would be the entire list of labels/label IDs, which isn't necessarily what I want. At the very least, when the form panel's load() method is called, the store needs to contain the model/record which matches the labelId stored to the combobox during the form load.

As a workaround, I could call the combo's doQuery() method immediately after the form load, to ensure that the combo's store gets loaded with the target record. That's seems hackish to me, though.

I guess I'm trying to figure out what the proper "MVC" methodology is for loading a form with a combobox and having the combo display the appropriate label for the combo's loaded value (keeping in mind that the combo's store uses queryMode: 'remote')...

Thanks for your replies/time/trouble.

skirtle
6 Jan 2012, 1:39 AM
So I dug into this a bit more and as I expected it got painful. If anyone comes up with an easier way to do this I'd be curious to see it.

So let's outline the scenario I'm targeting...


There's a combobox. There's too much data to load it all, so queryMode: 'remote' is set.
The initial value of the combobox is not necessarily empty. Perhaps it's being used as a grid cell editor or as part of a pre-populated form.
The displayField and valueField are different, with forceSelection enabled.
We also need to support clearing the value.


The problems occur because attempting to call setValue will reject the change if the value isn't in the store. There is one exception: if the store is partially through loading then it's clever enough to wait for the store to finish loading before deciding whether to reject the change.

On the face of it we can just call doQuery ourselves to kickstart the load. Problem is, the remote query needs to send the displayField value and what we have is the valueField value. This presumably is why the combobox doesn't handle this itself.

So we need a server that can cope with loading the values based on a valueField value, which is labelId in the context of the original question. Let's assume we've implemented that ok. Then we need to jump through some hoops in Ext:


Ext.define('MyComboBox', {
alias: 'widget.mycombobox',
extend: 'Ext.form.field.ComboBox',

// This override is purely so that we can clear the value, it is separate to the data loading problem
assertValue: function() {
var me = this,
value = me.getRawValue();

if (me.allowBlank && !value) {
me.clearValue();
}
else {
me.callParent();
}
},

setValue: function(value) {
var me = this,
store = me.getStore();

// Update these checks to reflect your data but note that setValue is called with an array internally
if (value && Ext.isString(value) && !me.findRecordByValue(value)) {
// Clear lastQuery so the combobox doesn't get confused about what data is loaded
me.lastQuery = null;

store.load({
params: {
// Can't just use 'query=value' as that needs the label, not labelId
labelId: value
}
});
}

// Call the original setValue, which will defer itself if the store is loading
me.callParent(arguments);
}
});

There is the potential to get into an infinite loop with this implementation. After the store loads it'll call setValue again and if the record is still missing it'll try to load it again.

Let me know what you think. In my testing this seemed to behave the way you wanted.

friend
6 Jan 2012, 4:42 AM
Eloquent, well written and spot-on, Skirtle.

Issues like this are a real wart on an otherwise brilliant framework. It's a shame that all the Sencha supplied MVC tutorials only walk you down the "happy path".

I would think the problem described in this thread is a very, very common, real-world scenario which others would have encountered by now...

Regardless, I'll consider this a dead-end issue and start coding workarounds. Thanks for understanding/feeling my pain.

:]

skirtle
6 Jan 2012, 6:00 AM
I don't think there's anything about these issues that relates to the MVC, they occur even if you don't go near the MVC. I'm pretty sure these aren't new to ExtJS 4 either, I think it's the same with previous versions.

I've come up against the forceSelection/allowBlank issue a few times now. Following our discussion I went to file a feature request and found it's already there. Added my support to it. Not sure if you can see the FR forum but if you can it's here:

http://www.sencha.com/forum/showthread.php?148518

The other issue, setting the value for remote data, strikes me as a really tricky one to fix 'properly'. While the workaround is quite short I don't see how they could build support for it into the framework. Perhaps rather than trying to 'fix it' they could provide slightly more convenient hooks so that the workarounds are less unpleasant, then give an example in the docs?