I am using EXTJS with an editorgridpanel and I am trying to to insert a combobox, populated with JsonStore.
Here is a snapshot of my code:
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;
};
The combobox is inserted in an editorgridpanel.
There's a renderer like this:
Ext.util.Format.comboRenderer = function(combo){
return function(value, metadata, record){
alert(combo.store.getCount()); //<== always 0!!
var record = combo.findRecord(combo.valueField || combo.displayField, value);
return record ? record.get(combo.displayField) : combo.valueNotFoundText;
}
};
When the grid is displayed the first time, instead of have the displayField, I have : "Value not found."
And I have the alert : 0 (alert(combo.store.getCount())) from the renderer.
But I can see in the console that the data have been correctly loaded!
Even if I try to reload the store from the renderer (combo.store.load();), I still have the alert (0)!
But when I select the combo to change the value, I can see the data and when I change the value, I can see the displayFiel!
I don't understand what's the problem?
Since now several days, I already tried all the solutions I found...but still nothing!
Any advice is welcome!
Yoong
The core of the problem is that you need to hook an listener which will execute after the grid store loaded. That listener will then convert the combo grid content to the displayField content instead of valueField.
Here is my resolution to this issue.
Ext.ns("Ext.ux.renderer");
Ext.ux.renderer.ComboBoxRenderer = function(combo, grid) {
var getValue = function(value) {
var record = combo.findRecord(combo.valueField, value);
return record ? record.get(combo.displayField) : value;
};
return function(value) {
var s = combo.store;
if (s.getCount() == 0 && grid) {
s.on({
load: {
single: true,
fn: function() {
grid.getView().refresh()
}
}
});
return value
}
return getValue(value)
}
};
You can use this renderer in your code like the following:
{
header: 'Header',
dataIndex: 'HeaderName',
autoWidth: true,
renderer: Ext.ux.renderer.ComboBoxRenderer(combo, grid),
editor: combo
}
This is a common issue. If you need a store value to display in the combo, handle the store's load event and select the appropriate value in the combo only after that. You shouldn't need a specific record just to render the combo up front.
I would recommend adding the field required from the combobox's store, to the grid's store, and change the renderer to use that field. (It does not have to be in the database)
and on the afteredit event of the grid, grab that value from the combobox's store and copy it to the grid's store.
This would yield better performance.
Actually, the problem seems to be that the grid renders the combobox values BEFORE the loading of the store data.
I found something here.
But when I applied the override, still the same problem...
The question i: how to defer the render of the combobox, waiting for the loading of the store?
Related
I have in my ExtJS 4.2.1 Application a grid with the following editable column:
text: 'Location',
dataIndex: 'LocationId',
width: 140,
renderer: function(value) {
var record = me.store.findRecord('LocationId', value);
return record.get('Description');
},
editor: {
xtype: 'combobox',
typeAhead: true,
triggerAction: 'all',
store: Ext.create('App.store.catalog.Location', {
autoLoad: true
}),
displayField: 'Description',
valueField: 'LocationId',
listConfig: {
width: 250,
loadingText: 'Searching...',
// Custom rendering template for each item
getInnerTpl: function() {
return '<b>{Code}</b><br/>(<span style="font-size:0.8em;">{Description}</span>)';
}
}
}
The combo has a renderer to display the Description of the LocationId selected.
Then, my grid has the feature 'Ext.grid.plugin.CellEditing' so I can edit just that column cell.
The problem that I have is when I press the "Update" button, the combo display value returns to the original it used to have, even if the LocationId in the record has the right value.
This is my code that gets fired when the user press the "Update" button.
me.rowEditing = Ext.create('Ext.grid.plugin.RowEditing', {
clicksToMoveEditor: 1,
autoCancel: false,
listeners: {
edit: function(editor, e) {
var record = e.record;
me.setLoading('Updating...');
App.util.Ajax.request({
noMask: true,
url: '/api/catalog/UpdateEmployeeLocation',
jsonData: record.getData(),
success: function(response, opts) {
var obj = Ext.decode(response.responseText);
if (obj.success) {
// commit changes (no save just clear the dirty icon)
me.getStore().commitChanges();
}
},
callback: function() {
me.setLoading(false);
}
});
}
}
});
The record is saved correctly in my database but the combo display value is not updated with the description that corresponds to the LocationId. If I reload the store from server again then It shows correctly.
So, there is something wrong with the renderer in my column that is not updating the value after I update my record.
Any clue on how to get around this?
Thanks.
You are setting dataIndex as 'LocationId' but no where you are changing the 'LocationId', you are just changing description and updating it in rendered method. Since there no change in 'LocationId', store doesn't consider it as dirty field and hence rendered function is not getting called. One quick and dirty way could be instead of using 'LocationId', create another field in the model say 'LocationIdchangeTraker'. Use 'LocationIdchangeTraker' instead of 'LocationId' in data index. It doesn't not effect your view because you are changing the value in reneerer function. Now whenever you update the function change the value of 'LocationIdchangeTraker' as shown below.
record.set('LocationIdchangeTraker',Ext.getId());
I have grid with RowEditing plugin.
Editor has 2 columns: one with combobox and another with disabled textfield.
I need to change textfield value after changing the combobox value.
I have combobox listener:
listeners = {
select: function (combo, records) {
var editorRecord = myGrid.getPlugin('rowEditPlugin').editor.getRecord();
editorRecord.data["SomeCol"] = "SomeValue";
}
}
But value in the textfield does not refresh until another calling of roweditor.
I need just to set text value to the cell, without updating store. And also if I click cancel button of roweditor, I need cell value returning to old one.
Thanks for your time, all replies and all help.
You can change it using the selection model of the grid,something like this :
{
text: 'Type',
dataIndex: 'type',
editor: {
xtype: 'combo',
typeAhead: true,
selectOnTab: true,
displayField:"name",
listeners: {
select: function(combo, records) {
myGrid= this.up('grid');
var selectedModel = this.up('grid').getSelectionModel().getSelection()[0];
//selectedModel.set('dataIndexOfNextCol', "Success");
val = combo.getValue();
if(val == 'type1'){
Ext.getCmp('textFld').setValue("success");
}
else{
Ext.getCmp('textFld').setValue("failure");
}
}
},
{
text: 'Item',
dataIndex: 'item',
editor: {
xtype: 'textfield',
id: 'textFld'
}
}
You have to commit the changes on update.So you can call edit event listener,
listeners: {
edit: function(editor, e) {
e.record.commit();
}
},
For reference , have a look at this DEMO
I have a combobox editor for a grid column. It is editable too. The store for the combobox has autoLoad config, set to false implying that when the user clicks on the combobox, the store is loaded. It works fine if I don't type in anything in the combobox and click on it. However, if I type something first in the combobox, then click outside and then again click on the combobox to load the dropdown, it doesn't render at all. It just shows loading and then doesn't display the drop down.
It's a very weird problem as I have similar comboboxes for other columns as well which work fine but they are not editable.
Is this anything to do with the editable config?
var contextDropDownStoreforFactGrid = Ext.create('Ext.data.Store', {
fields: [{name:'context',type:'string'}],
proxy: {
type: 'ajax',
url: context + '/FcmServlet',
extraParams: {
'action': 'getContextDropDownValues'
},
reader: {
type: 'json'
}
},
autoLoad: false /* load the store only when combo box is selected */
});
editor: {
xtype: 'combo',
store: contextDropDownStoreforFactGrid,
qureyMode: 'remote',
id: 'fact_contextId',
displayField:'context',
valueField: 'context',
vtype: 'alphanum',
listeners: {
beforeQuery: function(query) {
if (contextDropDownStoreforFactGrid.getCount() != 0) {
contextDropDownStoreforFactGrid.removeAll();
contextDropDownStoreforFactGrid.load();
}
}
}
},
renderer: function(value) {
var index = contextDropDownStoreforFactGrid.find('context', value);
if (index != -1) {
return contextDropDownStoreforFactGrid.getAt(index).data.context;
}
return value;
}
You have a spelling error in your combobox configuration:
qureyMode: 'remote',
should be
queryMode: 'remote',
This is probably causing the combobox not to load data from your store.
I have a combo box inside a form like:
xtype: 'combo',
id: 'example',
name: 'ax',
triggerAction: 'all',
forceSelection: true,
editable: false,
allowBlank: false,
fieldLabel: 'example',
mode: 'remote',
displayField:'name',
valueField: 'id',
store: Ext.create('Ext.data.Store', {
fields: [
{name: 'id'},
{name: 'name'}
],
//autoLoad: false,
proxy: {
type: 'ajax',
url: 'example.php',
reader: {
type: 'json',
root: 'rows'
}
}
}
})
I don't want that auto load because that's slow when i start.
But i want set a value to combo box when i click edit button and load value to combo
this.down('form').getForm().load({
url: 'load.php',
success:function(){
}
});
data from load.php like (name of combe is ax)
{ success:true , data : { ax: '{"id":"0","name":"defaults"}' } }
But that's not working. How can i do that thanks.
p/s: If i have autoLoad : true and data is { success:true , data : { ax: '0' } } that's work well. But that's slow when i start.
What you want to do is make sure the combo is loaded before you try to set it's value.
You could check if the combo's store has any data in it:
if(combo.getStore().getCount() > 0)
{
//the store has data so it must be loaded, you can set the combo's value
//doing a form.load will have the desired effect
}
else
{
//the store isn't loaded yet! You can't set the combo's value
//form.load will not set the value of the combo
}
If it does, you can just set the value. But more likely however it will not have been loaded.
You could also do something like this
//in the controller's init block
this.control({
"#myEditButton" : {click: this.loadForm}
});
//a function in your controller
loadForm: function(button)
{
var combo; //get your combo somehow, either via references or via the button
combo.getStore().load({
scope: this,
callback:
function(records, operation, success)
{
if(success)
{
//load your form here
}
}
});
}
I know that might seem like a lot of code, but it's the only way to know for sure if the combo was loaded. If it is not, you cannot set it's value.
A third option would just be to explicitly load the store before you open your view.
I've a combobox (e.g. change owner). When a user selects a value of the combobox, I prompt user if he is sure to change owner. If he clicks on 'Yes', I update the record in database. All works fine till this point.
Now, if the user selects a value and clicks 'No' on prompt (after selecting value from combo). The combo retains the new value and does not bring it back to the old value. I tried setValue/Load etc but none is setting back the old value on the click on No.
My Code looks like this
combo = new Ext.form.ComboBox({
store: storeJson,
xtype: 'combo',
displayField:'name',
valueField: 'id',
mode: 'local',
id: 'privateTo',
name: 'privateTo',
typeAhead: false,
triggerAction: 'all',
lazyRender: true,
editable:false,
listeners: {select: changeOwner}
});
var changeOwner = function(combo, record, index) {
Ext.MessageBox.confirm("Change Owner","Are you sure you want to change owner?",function(btn){
if (btn == "yes") {
Ext.Ajax.request({
url: 'somUrl',
method: 'PATCH',
success: function(result, request) {
msg('Owner changed', 'Owner changed');
},
failure: function(result, request) {
Ext.MessageBox.alert('Error', "Unable to change owner");
Ext.getCmp("customizationGrid").getStore().reload();
}
});
} else {
var d = Ext.getCmp('customizationGrid').getSelectionModel().selection;
var rec = combo.store.getById(d.record.json.privateTo);
Ext.getCmp("privateTo").setValue(rec.data.name);
}
});
}
Just remove the else { ... } block, it should take care of NO. :-)
The last selected value of your combo is already present in the select handler.
You can simply do :
...
} else {
combo.setValue(combo.startValue);
}