PDA

View Full Version : [OPEN]ComboBox's setValue call with a remotely loaded Store



devtig
22 Feb 2011, 12:35 AM
This is not a bug, but a question and hope that this override is not necessary anymore in Ext JS 4?

http://www.sencha.com/forum/showthread.php?75751-OPEN-42-ComboBox-s-setValue-call-with-a-remotely-loaded-Store

fmntf
22 Feb 2011, 1:23 AM
+1

evant
24 Feb 2011, 1:44 AM
There is sort of support for it already, the combo checks if the store is loading while setValue is called. If that happens, it defers the setValue call until later.

The thing I don't like about the linked thread is it all seems a bit hacky.

devtig
24 Feb 2011, 7:15 AM
That's a matter of how you define hacky. Off course the posted code makes sense. It does what it needs to do in logical way. To the average ExtJS user/developer however it's just a lot of code and a lot of logic for functionality that they/I would expect from the framework itself.

I hope you're not suggesting that where actually going to need a similar override in Ext 4?

abraxxa
5 May 2011, 12:49 AM
@evant: how is a combobox in an edit form supposed to work? Should it load all items and find the selected one by id? That's awefully slow for comboboxes with hundreds or even thousands of items where you'd normally load the items based on a search term.

devtig
5 May 2011, 12:53 AM
No, it should not load all items. Your server should not have sent all items, but only the item with the id requested.

abraxxa
5 May 2011, 3:12 AM
@devtig: but the ExtJS combobox doesn't load a single item without the override.

devtig
5 May 2011, 3:47 AM
why?

abraxxa
5 May 2011, 5:16 AM
The override adds this functionality because it's missing in ExtJS's Combobox class.

devtig
5 May 2011, 7:05 AM
No it doesn't. The override adds the functionality of being able to call setValue on a ComboBox with a remotely loaded store that isn't loaded yet.

abraxxa
5 May 2011, 9:17 AM
I'm talking about the feature that starts with the code from Condor on page two of the thread.
It loads just the single record by id to shows its name instead of its id.

cdomigan
7 Aug 2011, 2:44 PM
+1 for this bug please

manyu.tomar
8 Aug 2011, 4:38 AM
ya but even in extjs 4 , the problem is same the combo does not show the default value in the edit form because the store has not yet been loaded .

nomatter
30 Aug 2011, 10:57 PM
How about something like that (controlled by a parameter in the original ComboBox)?



Ext.define('myapp.SearchCombo', {
extend: 'Ext.form.ComboBox',
alias: 'widget.searchcombo',

findRecord: function(field, value) {
var ds = this.store,
idx = ds.findExact(field, value);

// lets get the record!
if(idx === -1) {
this.store.on('load', Ext.bind(this.setValue, this, [value]), null, {single: true});
ds.load({
params: {
id: value
}
});
return;
}

return idx !== -1 ? ds.getAt(idx) : false;
}
});


I use this Version to allow setValue without a loaded Store (e.g. when i have a foreign key to some huge dataset in a form record).

For use in the default ComboBox we would of course need some config params and maybe also some further checks.

mmarti
16 Feb 2012, 6:54 AM
+1

Any news for extjs4?

KajaSheen
1 Mar 2012, 6:14 AM
Hi all,

I found a little different solution. I pass an object in the record data and extract a record from it:



record: {id: 1, text: 'text', combobox: {id: 5, text: 'default_value'}}


Then I added this to the setValueFunction:



Ext.override(Ext.form.ComboBox, {
setValue: function(value, doSelect) {

var me = this,
valueNotFoundText = me.valueNotFoundText,
inputEl = me.inputEl,
i, len, record,
models = [],
displayTplData = [],
processedValue = [];

if (me.store.loading) {
// Called while the Store is loading. Ensure it is processed by the onLoad method.
me.value = value;
me.setHiddenValue(me.value);
return me;
}

// This method processes multi-values, so ensure value is an array.
value = Ext.Array.from(value);

// Loop through values
for (i = 0, len = value.length; i < len; i++) {
record = value[i];
if (!record || !record.isModel) {
record = me.findRecordByValue(record);
}

// is object, try to convert to model and push
if (!record && typeof(value[i]) == 'object') {

var model = me.store.getProxy().getModel().modelName;
record = Ext.create(model, value[i]);
}

// record found, select it.
if (record) {
console.log(record);

models.push(record);
displayTplData.push(record.data);
processedValue.push(record.get(me.valueField));
}
// record was not found, this could happen because
// store is not loaded or they set a value not in the store
else {
// If we are allowing insertion of values not represented in the Store, then set the value, and the display value
if (!me.forceSelection) {
displayTplData.push(value[i]);
processedValue.push(value[i]);
}
// Else, if valueNotFoundText is defined, display it, otherwise display nothing for this value
else if (Ext.isDefined(valueNotFoundText)) {
displayTplData.push(valueNotFoundText);
}
}
}

// Set the value of this field. If we are multiselecting, then that is an array.
me.setHiddenValue(processedValue);
me.value = me.multiSelect ? processedValue : processedValue[0];
if (!Ext.isDefined(me.value)) {
me.value = null;
}
me.displayTplData = displayTplData; //store for getDisplayValue method
me.lastSelection = me.valueModels = models;

if (inputEl && me.emptyText && !Ext.isEmpty(value)) {
inputEl.removeCls(me.emptyCls);
}

// Calculate raw value from the collection of Model data
me.setRawValue(me.getDisplayValue());
me.checkChange();

if (doSelect !== false) {
me.syncSelection();
}
me.applyEmptyText();

return me;
}
});



This avoids a couple of the mentioned problems, as the value is available immediatly to be submitted, and no extra request is made to the server to fetch the single record. Of course there are checks missing, but for now this works for me.

vishalg
22 Mar 2012, 2:12 PM
Wow this bug has been present since Ext 2. I have gotten around by pre-loading all my data stores at the start of the application but I would like to get away from that at some point.

Please fix this in the framework.

+1

devnullable
28 Mar 2012, 8:49 PM
This really should be fixed.

Sencha devs! Any comments?

RandellB
29 Mar 2012, 5:05 AM
I've been wrestling with this one too. One slightly less hacktastic way I found was to have the proxy's "afterRequest" function set the combobox's value, because then you know the store has been loaded by that point. Keep in mind that it does not work if you have paging enabled in the proxy and your selected value is not in the first page of data. You might be able to add more logic in "afterRequest" to get around that though?

I do agree that something like this should be base functionality, especially when using paging. /:)



function CreateComboBoxWithStore(currValue, cbId)
{
var cb = Ext.create('Ext.form.ComboBox', {
xtype: 'combo',
id: cbId,
store: Ext.create('Ext.data.Store', {
autoLoad: true,
fields: [
{
name: 'PRIMARYKEYCOLUMN'
},
{
name: 'DISPLAYCOLUMN'
}
],
pageSize: 100,
remoteFilter: true,
proxy: {
type: 'ajax',
actionMethods: {
read: 'POST'
},
api: {
read: ---servlet call to get data---
},
reader: {
type: 'json',
root: 'data'
},
afterRequest: function (request, success)
{
cb.setValue(currValue);
}
}
}),
queryMode: 'remote',
displayField: 'DISPLAYCOLUMN',
valueField: 'PRIMARYKEYCOLUMN'
});


return cb;
}

malstroem
29 Mar 2012, 6:28 AM
Hi there,

IŽd like to contribute my own little hackaround solution for this:
Use the "valueNotFoundText" for what you want to display http://www.sencha.com/forum/images/smilies/4.gif

I was doing lot of effort with load-listeners (ComboStore load) and deferring the setValue until the store had been loaded... but every once in a while I faced problems again. Thats why I am not loading the comboStore at all anymore, but still can assign the right value to it... here is how:

When loading e.g. a form with a combobox, I just load the id for the ComboBoxValue AND the matching display name for this record. Then I instaninate my combobox with:



xtype: 'combobox',
queryMode:'remote',
forceSelection: true,
store:Ext.create('Ext.data.Store', {
model:'MyComboModel'
}),
name: 'category_id',
valueField:'category_id',
value: (this.record.category_id || ''),
valueNotFoundText: (this.record.category_name || ''),
hiddenValue: 'category_id',
displayField: 'category_name'


That is how the combo shows the right valuetext without even having loaded the ComboStore... I know that this is a hack - but it works perfectly for me!

Cheers Thomas

ngrover
24 Apr 2012, 10:44 PM
How about something like that (controlled by a parameter in the original ComboBox)?



Ext.define('myapp.SearchCombo', {
extend: 'Ext.form.ComboBox',
alias: 'widget.searchcombo',

findRecord: function(field, value) {
var ds = this.store,
idx = ds.findExact(field, value);

// lets get the record!
if(idx === -1) {
this.store.on('load', Ext.bind(this.setValue, this, [value]), null, {single: true});
ds.load({
params: {
id: value
}
});
return;
}

return idx !== -1 ? ds.getAt(idx) : false;
}
});


I use this Version to allow setValue without a loaded Store (e.g. when i have a foreign key to some huge dataset in a form record).

For use in the default ComboBox we would of course need some config params and maybe also some further checks.

I had to tweak you're solution for ExtJS 4 RC3:



findRecord: function(field, value) {
var ds = this.store;
var idx = ds.findExact(field, value);
if(idx === -1 && !this.initialRecordFound) {
this.initialRecordFound = true;
this.store.on({
load: {
fn: Ext.Function.bind(function(value) {
if (this.forceSelection) {
this.setValue(value);
}
this.store.removeAll();
}, this, [value]),
single: true
}
});
ds.load({
params: {
id: value
}
});
}
return idx !== -1 ? ds.getAt(idx) : false;
}

dbrin
2 May 2012, 11:35 PM
Tweaked it just a tad to enable the use of queryParam:

findRecord: function(field, value) {
var ds = this.store,params ={},
idx = ds.findExact(field, value);
if(idx === -1 && !this.initialRecordFound && this.queryMode === 'remote') {
this.initialRecordFound = true;
this.store.on({
load: {
fn: Ext.Function.bind(function(value) {
if (this.forceSelection) {
this.setValue(value);
}
this.store.removeAll();
}, this, [value]),
single: true
}
});
params[this.queryParam]=value;
ds.load({params:params});
}
return idx !== -1 ? ds.getAt(idx) : false;
}



you can now use it in an override to effect all of you remote loading combos, just wrap the function in

Ext.override(Ext.form.field.ComboBox, {
..my overriden function
});

ngd
3 Jun 2012, 5:47 PM
@Evant: what is the solution? Is there one yet. It's now 4.1.1.

wboard
10 Jun 2012, 2:20 AM
I still use this as workaround (works fine, http://www.sencha.com/forum/showthread.php?131771-Ext.ux.form.AutoCombo-Combobox-that-populates-choices-when-setting-value),

is there any progress in fixing this issue properly??

pward123
18 Jun 2013, 3:11 AM
+1 - amazed this bug hasn't been addressed since it was originally reported almost 4 years ago

chocis
7 Jul 2013, 4:57 AM
Yeah, this should be implemented by default in ExtJS, because I hate using hacky overrides.

cmendez21
11 Sep 2013, 5:34 AM
So far i encounter the same issue about setValue and the problem is all in : findRecord: function(field, value) {

Original code:


findRecord: function(field, value) {
var ds = this.store,
idx = ds.findExact(field, value);
return idx !== -1 ? ds.getAt(idx) : false;
},


and the problem is I set a Value (int) type and the function search it as text but not by the proper data type according on the MODEL , so i changed so far to this, and worked according my needs , perhaps it needs more work .


findRecord: function(field, value) { // EDITED BY cmendez21
var ds = this.store;
var Fieldsx = ds.model.getFields();
var tmpval = undefined;
for (var i=0;i<Fieldsx.length;i++){
if (Fieldsx[i].name == field){
if (ds.model.getFields()[0].type.type =="int"){
tmpval = parseInt(value);
} else if (ds.model.getFields()[0].type.type =="float"){
tmpval = parseFloat(value);
} else {
tmpval = value;
}
break;
}
}
//var idx = ds.findExact(field, value, 0); //Added 0 at the end
if (tmpval!=undefined){
var idx = ds.findExact(field, tmpval, 0); //Added 0 at the end
return idx !== -1 ? ds.getAt(idx) : false;
} else {
return false;
}
},


hope this helps for anyone

lawrence_o
20 Dec 2013, 2:33 AM
I had behavior I feel is incorrect but maybe I am missing something?

Given, window w defining combo c with store s. Store s has autoLoad: true;

Window w has a method to init form values of which combo c is an item.
I noticed that when shown, combo c did not have its value set.

Debugging revealed that combo setValue method has this:


if (me.store.loading) {
blah blah
}

But it appeared that the store.load was not yet called yet is is autoLoad: true!! I did not expect this. I would assume creation of the combo with autoLoad store to have completed and that the store.load would have been called at least before returning the initComponent / constructor calls!

I guess that changing the above code to


if (me.store.loading || me.store.getCount == 0) {
blah blah
}
would solve the problem... Perhaps by introducing a new config like e.g. deferOnZeroCount or something...

Thanks for your feedback.
Kind regards
Lorenzo

Hacker-CB
10 Feb 2014, 8:03 AM
When this problem will be fixed?

filippo.ferrari
28 Apr 2014, 11:35 PM
When this problem will be fixed? +1

Skay
6 Oct 2015, 1:29 PM
Probably never) I faced this problem in ExtJS 6

d.zucconi
19 Nov 2015, 2:48 AM
+ 1

ssamayoa
25 Jan 2017, 12:47 PM
I'm starting a new app with SA + ExtJS 6.2 and need to use a combo with a huge table (products) in edit mode, I mean I need the selected item in the combo after the record is loaded without the need of load thousands of records but found that this problem stills here.

:( :( :( :(

I just finished an application using DevExtreme in which this problem is *SOLVED*, dxSelect -the name of combobox in DevExtreme- calls "bykey" to retrieve the value to be shown).

How a less evolved framework can have a better solution?

Sad...

abraxxa
25 Jan 2017, 1:53 PM
I coded a solution for this problem myself.
If also sets some attributes to different default values which make more sense like not allowing items not included in the store (forceSelection: true).

Improvements welcome!
My last one was '&& !store.autoLoad' to enable the usage of one store for multiple naccomboboxes.



Ext.define('NAC.form.field.ComboBox', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.naccombobox',


autoLoadOnValue: true,
autoSelect: false,
anyMatch: true,
forceSelection: true,
minChars: 3,
typeAhead: true,
// this adds an optimization to autoLoadOnValue by only loading the
// record for the selected value
setValue: function (value) {
var store = this.getStore();
if (!Ext6.isEmpty(value) && store !== null && !store.isEmptyStore && !store.isLoaded() && !store.autoLoad) {
// console.log('load only selected record for ' + value + ' into store ' + store.getModel().getName());
/* use the store
* because it supports prefetching on the server side
* which we need for the displayField
*/
var idProperty = store.getModel().idProperty;
store.addFilter({
exactMatch: true,
property: idProperty,
value: value
}, true);
store.onAfter('load',
function (eventName, args, options) {
this.clearFilter(true);
}, store, { single: true });
this.callParent(arguments);
} else {
this.callParent(arguments);
}
}
});

ssamayoa
25 Jan 2017, 1:57 PM
Just about to implement something like yours but perhaps using the model.

Thanks for the code!

Regards!