PDA

View Full Version : EXTJS 3.2.1 - Editorgridpanel - combobox using JSON store. [SOLVED]



kiminox
18 May 2010, 6:58 PM
Hi,

I have an editor grid panel with a combobox.
I cannot display the text instead of the value of the options.
After investigation and search, it seems that the problem is that the renderer to render the cell is called BEFORE the store's loading.
I already stried the patch suggested in the ComboBox FAQ and other solution but still have the same problem.
Here is a snapshot of my code for the store :

kmxgz.ordercmpappro.prototype.getCmpapproStore = function(my_url) {
var myStore = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url: my_url
, method: 'POST'
})
, reader: new Ext.data.JsonReader({
root: 'rows',
totalProperty: 'total',
id: 'list_cmpappro_id',
fields: [
{name: 'list_cmpappro_id', mapping: 'list_cmpappro_id'}
, {name: 'list_cmpappro_name', mapping: 'list_cmpappro_name'}
]
})
, autoLoad: true
, id: 'cmpapproStore'
, listeners: {
load: function(store, records, options){
//store is loaded, now you can work with it's records, etc.
console.info('store load, arguments:', arguments);
console.info('Store count = ', store.getCount());
}
}
});
return myStore;
};The combo:

kmxgz.ordercmpappro.prototype.getCmpapproCombo = function(my_store) {
var myCombo = new Ext.form.ComboBox({
typeAhead: true,
lazyRender:false,
forceSelection: true,
allowBlank: true,
editable: true,
selectOnFocus: true,
id: 'cmpapproCombo',
triggerAction: 'all',
fieldLabel: 'CMP Appro',
valueField: 'list_cmpappro_id',
displayField: 'list_cmpappro_name',
hiddenName: 'cmpappro_id',
valueNotFoundText: 'Value not found.',
mode: 'local',
store: my_store,
emptyText: 'Select a CMP Appro',
loadingText: 'Veuillez patienter ...',
listeners: {

// 'change' will be fired when the value has changed and the user exits the ComboBox via tab, click, etc.
// The 'newValue' and 'oldValue' params will be from the field specified in the 'valueField' config above.
change: function(combo, newValue, oldValue){
console.log("Old Value: " + oldValue);
console.log("New Value: " + newValue);
},

// 'select' will be fired as soon as an item in the ComboBox is selected with mouse, keyboard.
select: function(combo, record, index){
console.log(record.data.name);
console.log(index);
}
}

});
return myCombo;
};And the renderer in the column model for the grid:


[...]

,{
id: columnName2,
header: columnNameHeader2,
dataIndex: columnName2,
//renderer: Ext.util.Format.comboRenderer(_self.kmx_cmpappro_combo),
align: _KMX_EXT_CSS_LEFT_,
width: 100,
sortable: false,
hidden: false,
resizable: true,
filter: false,
editor: _self.kmx_cmpappro_combo,
editable: true,
menuDisabled: true,
hideable: true,
fixe: false,
}
[...]And the patch:

// create reusable renderer for ComboBox
Ext.util.Format.comboRenderer = function(combo){
return function(value){
var record = combo.findRecord(combo.valueField || combo.displayField, value);
return record ? record.get(combo.displayField) : combo.valueNotFoundText;
}
}

/**
* @class Ext.grid.ComboColumn
* @extends Ext.grid.Column
* <p>A Column definition class which renders a ComboBox field based on the Column's configured
* editor. See the {@link Ext.grid.ColumnModel#xtype xtype} config option of {@link Ext.grid.ColumnModel}
* for more details.</p>
*/
Ext.grid.ComboColumn = Ext.extend(Ext.grid.Column, {
constructor: function(cfg){
Ext.grid.ComboColumn.superclass.constructor.call(this, cfg);
this.renderer = Ext.util.Format.comboRenderer(this.editor.field ? this.editor.field : this.editor);
}
});

// apply the custom column type to the prototype
Ext.apply(Ext.grid.Column.types, {
combocolumn: Ext.grid.ComboColumn
});

// Form submits displayField instead of valueField: the store has to be loaded BEFORE setValue().
Ext.override(Ext.form.ComboBox, {
setValue : function(v){
//begin patch
// Store not loaded yet? Set value when it *is* loaded.
// Defer the setValue call until after the next load.
alert('this.store.getCount() = ' + this.store.getCount());
if (this.store.getCount() == 0) {
this.store.on('load',
this.setValue.createDelegate(this, [v]), null, {single: true});
return;
}
//end patch
var text = v;
if(this.valueField){
var r = this.findRecord(this.valueField, v);
if(r){
text = r.data[this.displayField];
}else if(this.valueNotFoundText !== undefined){
text = this.valueNotFoundText;
}
}
this.lastSelectionText = text;
if(this.hiddenField){
this.hiddenField.value = v;
}
Ext.form.ComboBox.superclass.setValue.call(this, text);
this.value = v;
}
});

Condor
18 May 2010, 11:13 PM
You need to load the combobox store first and then the grid store.

(or you need to return both the id and the text in the grid store and add some extra code to keep both in sync)

kiminox
18 May 2010, 11:37 PM
Actually, the combobox store is loaded using ajax call: so, it's asynchronous.
That means I cannot control the time of loading: even if I load the store of the combobox BEFORE loading the grid, the problem will be the same.
But your second idea:"to return both the id and the text in the grid store and add some extra code to keep both in sync", what does it mean?
Where shoud I return the id and the text?
Thank you

Condor
18 May 2010, 11:48 PM
Sure you can:

comboboxStore.load({
callback: function(){
gridStore.load();
}
});
(and remove the autoLoads)

kiminox
19 May 2010, 11:42 PM
ok thank you Condor but if I have more than 1 combobox, do how to do?
This is a problem: one Ajax call for each combobox (per cell in the editor grid) but I should load the grid only when all the Ajax calls are done no?

kiminox
20 May 2010, 12:08 AM
Actually, I found a way: load the stores of the comboboxes with the callback functions...and the last callback loads the grid!
Example of my code:

// load combobox stores BEFORE grid
this.kmx_cmpappro_store.load({
callback: function() {
_self.kmx_order_store.load({
callback: function() {
my_grid.store.load({params:{start:0, limit:_self.kmx_pagination_page_size}});
}
});
}
});Somebody has a better way to achieve that?
It works but well, I am not really satisfied.
And if I click on the "load" button of the grid, I have to do something again ?
Thank you Condor anyway.

Condor
20 May 2010, 12:20 AM
I would rewrite my server app to return all the data (comboboxes + grid) in one request.
Then you can load the comboboxes first and then the grid with the retured data.

kiminox
20 May 2010, 12:38 AM
Actually, that's what I did: I defined a reusable editor grid panel with some common buttons/actions: each child class must redefine the column model, the stores, the record, the url to save/delete...
20586

Fortunaly!
If somebody is interested, I can share my code with you: that's why I love OPEN SOURCE!

kiminox
20 May 2010, 12:48 AM
Actually,

There's a problem with the reloading button/action: when I modified the data, the grid auto-reloads itself (or when I click on the button refresh). Then the grid refreshs itself but without refreshing the combobox stores.
Question: how can I change the behavior of the refresh action? Do I override the load button/refresh ? How?
I found something:

Ext.override(Ext.data.Store, {
startAutoRefresh : function(interval, params, callback, refreshNow){
if(refreshNow){
this.load({params:params, callback:callback});
}
if(this.autoRefreshProcId){
clearInterval(this.autoRefreshProcId);
}
this.autoRefreshProcId = setInterval(this.load.createDelegate(this, [{params:params, callback:callback}]), interval*1000);
},
stopAutoRefresh : function(){
if(this.autoRefreshProcId){
clearInterval(this.autoRefreshProcId);
}
}
});

But it's a quite complex no? Thanks

Condor
20 May 2010, 1:09 AM
I would recommend:

var grid = new Ext.grid.EditorGridPanel({
...
viewConfig: {
...
onDataChange: function(store){
combobox1.store.loadData(store.reader.jsonData);
combobox2.store.loadData(store.reader.jsonData);
this.constructor.prototype.onDataChange.apply(this, arguments);
}
}
});

kiminox
21 May 2010, 1:24 AM
Well,

It seems that it doesn't work.
I added this parameter to my grid and wrote an alert:



kmxgz.ordercmpappro.prototype.run = function () {
var _self = this;
var myViewConfig = {
onDataChange: function(store){
alert('onDataChange');
_self.kmx_cmpappro_store.loadData(store.reader.jsonData);
_self.kmx_order_store.loadData(store.reader.jsonData);
this.constructor.prototype.onDataChange.apply(this, arguments);
}
};
// -----------------------------------------
// LAST STEP: render the grid, NOT BEFORE!
// -----------------------------------------
// load combobox stores BEFORE grid
this.kmx_cmpappro_store.load({
callback: function() {
_self.kmx_order_store.load({
callback: function() {
kmxgz.factory.kmxEditorGridPanel.prototype.run.call(_self, myViewConfig);
}
});
}
});
};

When I create the grid:


_

[...]

alert(print_r(myViewConfig)); <== I can see the content of the object with this alert
kmx_object_grid = new Ext.grid.EditorGridPanel({
id: _self.kmx_gridpanel_id
, title: _self.kmx_grid_title_text
[...]
, viewConfig: myViewConfig
[...]
});

But when I changed something in the grid or when I click on the reload/refresh button, I can see the Ajax call to reload the data for the grid but not the ajax calls for the combobox.

I think I don't have a special big application, just an application: I mean it's probably a common issue no?
Did somebody meet that before?
Is there anything in EXTJS to solve that?
I think about the callback: but how to use it in this case?
Thank you

Condor
21 May 2010, 4:53 AM
No, this solution requires that the grid data also contains the data for the comboboxes.

kiminox
24 May 2010, 7:47 AM
Thank you Condor.
My problem is actually half-solved...
but I will try to find a solution.

ravi9999
4 Feb 2013, 6:06 AM
hi kiminox, i am ravi please help me in download data from grid to excel data sheet ,