I have a combobox whose values I need to populate via the json formatted string I am retrieving via AJAX call.
Here's the string which I have retrieved (stored in data)
{
"a2m":
[
"a2mMeeting",
"sugar"
]
}
The combobox must contain a2m (this is the only name in the string as of now)
Here's what I have been trying:
Approach 1:
this.initAjaxCall = Ext.Ajax.request({
url : 'indigo/restproxy/get/v1/applications/list',
method:'GET',
scope : this,
success : function(result, request) {
var data = Ext.decode(result.responseText);
Object.keys(data).forEach(function(key) {
console.log(key);
Ext.getCmp('appCombo').getStore().add({appName : key});
})
}
});
this.appcombo = Ext.create('Ext.form.field.ComboBox', {
id : 'appCombo',
store: this.appStore
});
this.appStore = Ext.create('Ext.data.Store', {
fields : [
{
name : 'appName',
type : 'string'
}
]
});
Approach 2:
this.appStore = Ext.create('Ext.data.Store', {
fields: ['appName'],
proxy: {
type: 'ajax',
url: 'indigo/restproxy/get/v1/applications/list',
method : 'GET'
},
listeners: {
load: function(store, records, successful, eOpts ) {
store.insert(0, {
'appName' : 'yellow' //Trying to populate randomly using this method.
})
}
}
})
this.appcombo = Ext.create('Ext.form.field.ComboBox', {
id : 'appCombo',
store: this.appStore
});
In both the cases the combobox doesn't load any value and I am unable to see any value in the dropdown menu.
EDIT 1: When I printed the store.getCount() in the console after adding one element, the value is shown as 1 (checked by adding two elements and it does show 2). This implies the values are being added to the store but not being shown in the combobox dropdown menu. Kindly suggest how to resolve this.
SOLUTION:
The solution as it turns out was by adding a statement queryMode : 'local'. I don't understand why this was creating an issue. Anyone willing to shed light on this is welcome to do so.
As you have proven the store is being populated. However you haven't set a display field on the combo box.
this.appcombo = Ext.create('Ext.form.field.ComboBox', {
id : 'appCombo',
store: this.appStore,
displayField: 'appName'
});
displayField: The underlying data field name to bind to this ComboBox. Defaults to: 'text'
valueField: The underlying data value name to bind to this ComboBox. Defaults to match the value of the displayField config.
Related
I try to fill the form with the response data from the backend,but my formpanel contains combobox,how could load the data into combobox.
I tried to use the method setValues() in formpanel,and I set the displayField and valueField, but the combobox still shows [object,object].
Here is my store code
Ext.define('Servicemsg.directory.AppSystemStore', {
extend:'Ext.data.Store',
fields:['id','code','name','comments','lastupdatetime'],
proxy:{
type:'ajax',
url:'',
reader:{
type:'json',
}
},
})
Here is the code of combobox
{
xtype: 'combobox',
label: 'Provider',
name:'provider',
store:Ext.create('Servicemsg.directory.AppSystemStore'),
displayField:'name',
valueField:'id',
autoLoadOnValue:true
}
Here is my request code
form.load({
url:'#url#'+data.id,
success:function (form,result,data) {
form.setValues(result.result);
}
});
Response data like this
{
name:'',
text:'',
id:'',
provider:{
id:'',
name:'',
}
}
You can grab the combobox of the form and add the values to the combobox's store manually. Once the combobox store has the values, you can set a value to the combobox if applicable.
form.load({
url:'#url#'+data.id,
success:function (form,result,data) {
const combobox = form.down('combobox');
combobox.getStore().add(results.providers);
combobox.setValue(/*value that the comobobox should show*/);
form.setValues(result.result);
}
});
It seems like in this case using a store is overkill. Considering you have an
empty proxy and are just using it to try and load data into your combobox.
Instead you should create a View Model and with a property comboBoxData. You should
then bind this value to the data property on your combox box.
This is what your model would like like
Ext.define('Fiddle.view.main.MainModel', {
extend: 'Ext.app.ViewModel',
data: {
comboBoxData: null
}
});
This is what your combo box should look like
{
xtype: 'combobox',
label: 'Provider',
name: 'provider',
displayField: 'name',
valueField: 'id',
autoLoadOnValue: true,
bind: {
data: '{comboBoxData}'
}
}
This is what your success function would look like.
(How you get the view model will depend on where this is being called)
success: function (form, result, data) {
var vm = this.lookupViewModel();
vm.set('comboBoxData', result.result);
}
You may have to tweak this for you specific implementation. But, hopefully this gives you a good idea of the best approach for this.
I'm trying to get a return value of my json store to setValue in my combobox. The value of this json value is the current selected option in the database. My this.selectedCat store returns the current category value of the record where it's working in. my this.store returns all the available select options.
var fields = {
fields: ['text']
};
var notfields ={
fields:['category']
}
this.store = new GO.data.JsonStore({
url: GO.settings.modules.infoscherm.url + 'json.php',
baseParams:{
task:'selects'
},
root: 'results',
fields: fields.fields
});
this.selectedCat = new GO.data.JsonStore({
url: GO.settings.modules.infoscherm.url + 'json.php',
baseParams: {
task:'currentselectedcat',
id:'id'
},
root: 'results',
fields: notfields.fields
});
this.category = new Ext.form.ComboBox({
name: 'category',
width: 100,
store: this.store,
fieldLabel: "Categorie",
displayField:'text',
triggerAction: 'all',
editable: false,
selectOnFocus:true,
value: this.selectedCat //returns [object Object] naturally
});
Am I doing something wrong?
I think you need to start listening the "load" event of "Ext.data.JsonStore".
And the attached handler of "load" event will contain code to select any particular value by using "setValue" function of combobox object...
P.S. : Store values are made available in combobox drop down menu only if you configured the "store" config of combobox to the store object, in your case, you need to implement some logic within the handler of "load" event.
Using Ext.ux.grid.FiltersFeature, I have remote filters and I am trying to write a function to apply a date filter on a grid column programmatically (rather than clicking on the filter drop down menu in the column header). The first time I run the function the grid store gets reloaded without the filter. When I run the function a second time (and every time thereafter) it works totally fine, the store reloads with the filters. Here is the gist of the function I have:
// a filter object for testing
aFilter = {type: 'date', field: 'a_date_field', comparison: 'gt', value: '2012-03-08 00:00:00'}
var grid = Ext.create('Ext.grid.Panel', {
store: store,
features: [{
ftype: 'filters',
}],
columns[{
header: 'ID',
dataIndex: 'id',
itemId: 'id',
width: 40,
}, {
xtype: 'datecolumn',
header: 'Date',
dataIndex: 'a_date_field',
itemId: 'a_date_field',
width: 75,
format:'j-M-Y',
filterable: true
}],
listeners: {
'afterrender': function() {
// Need to create the filters as soon as the grid renders
// rather than waiting for the user to click on the header
grid.filters.createFilters();
}
},
bbar: [{
text: 'Do a filter',
handler: function() {
// get the filter that is attached to the grid
var gridFilter = grid.filters.getFilter(aFilter.field);
// have to do this to create a menu for this filter
gridFilter.init({dataIndex: aFilter.field, type: aFilter.type, active: true});
// if this column is a date filter column
if (gridFilter.type == 'date') {
var dateValue = Ext.Date.parse(aFilter.value, 'Y-m-d H:i:s');
if (filter.comparison == 'gt') {
gridFilter.setValue({after: dateValue});
} else {
gridFilter.setValue({before: dateValue});
}
}
}
}
});
I also found that this function works the first time if I click on any grid header menu before I run the function.
I've been trying to find out what changes are made to the grid which make the filter work after the first attempt fails or what clicking on any grid header does to make it work. But nothing I add seems to fix it so it will run the first time. Has anyone implemented this successfully?
I have workaround:
bbar: [{
text: 'Do a filter',
handler: function() {
var grid = this.up('grid');
var dateValue = Ext.Date.parse(aFilter.value, 'Y-m-d H:i:s');
var value = aFilter.comparison == 'gt' ? {after: dateValue} : {before: dateValue};
var gridFilter = grid.filters.getFilter(aFilter.field);
if (!gridFilter) {
gridFilter = grid.filters.addFilter({
active: true,
type: aFilter.type,
dataIndex: aFilter.dataIndex,
});
gridFilter.menu.show();
gridFilter.setValue(value);
gridFilter.menu.hide();
} else {
gridFilter.setActive(true);
}
Ext.Function.defer(function(){
gridFilter = grid.filters.getFilter(aFilter.field);
gridFilter.setValue(value);
}, 10);
}
}]
As you can see I actually apply filter 2 times.
As an update, I expanded this function and modified it to work with ExtJS 4.1.1
Here is an example of the function to set grid filters dynamically (without the user needing to click on the menu items). Afterwards, the filtered items will be visible to the user in the grid column header menus as if he clicked on them and set them manually.
The "grid" argument is a grid with FiltersFeature that you want to filter. The other argument is an array of "filter" objects (I'll show an example below), the function simply applies all the passed "filter" objects to the grid.
doGridFilter: function(grid, filters) {
// for each filter object in the array
Ext.each(filters, function(filter) {
var gridFilter = grid.filters.getFilter(filter.field);
gridFilter.setActive(true);
switch(filter.data.type) {
case 'date':
var dateValue = Ext.Date.parse(filter.data.value, 'm/d/Y'),
value;
switch (filter.data.comparison) {
case 'gt' :
value = {after: dateValue};
break;
case 'lt' :
value = {before: dateValue};
break;
case 'eq' :
value = {on: dateValue};
break;
}
gridFilter = log.filters.getFilter(filter.field);
gridFilter.setValue(value);
gridFilter.setActive(true);
break;
case 'numeric':
var value;
switch (filter.data.comparison) {
case 'gt' :
value = {gt: filter.data.value};
break;
case 'lt' :
value = {lt: filter.data.value};
break;
case 'eq' :
value = {eq: filter.data.value};
break;
}
gridFilter = log.filters.getFilter(filter.field);
gridFilter.setValue(value);
gridFilter.setActive(true);
break;
case 'list':
gridFilter = log.filters.getFilter(filter.field);
gridFilter.menu.setSelected(gridFilter.menu.selected, false);
gridFilter.menu.setSelected(filter.data.value.split(','), true);
break;
default :
gridFilter = log.filters.getFilter(filter.field);
gridFilter.setValue(filter.data.value);
break;
}
});
}
Here's an example of a "filter" object array.
// an example of a "filters" argument
[{
field: 'some_list_column_data_index',
data: {
type: 'list',
value: 'item1,item2,item3,item4,item5,item6,item7'
}
}, {
field: 'some_date_column_data_index',
data: {
type: 'date',
comparison: 'gt',
value: '07/07/2007'
}
}]
One caveat, you need to "create" the filters manually before using this function. Normally FiltersFeature grid filters are "created" the first time a user clicks on one of them, that may not happen if the user just wants to apply one of these predefined filters.
That can be handled easily by including this afterrender listener in the gridpanel.
listeners: {
// must create the filters after grid is rendered
afterrender: function(grid) {
grid.filters.createFilters();
}
}
Just add
filter: true
to grid columns description like this:
me.columns = [
{header:"Name", dataIndex:"name", editor:"textfield", filter: true},
];
if you want to get the filter work after the first attempt, first instance create.
Here is something that may be worth looking into. It seems that the filters plugin is listening for menucreate event to initialize the filters. I wonder if menu create event is deferred until necessary and hence the filters don't get initialized?
/**
* #private Handle creation of the grid's header menu. Initializes the filters and listens
* for the menu being shown.
*/
onMenuCreate: function(headerCt, menu) {
var me = this;
me.createFilters(); //<------
menu.on('beforeshow', me.onMenuBeforeShow, me);
},
Do you want to apply grid filter or may be store.filter() capability would suit you better? In this case just filter the store, and grid will display filtered records.
I discovered another way to implement this. It appears that grid features are only bound to the grid after the grid is rendered. This means that any setup of the filter will not take effect until after the grid is rendered. The initial load of the store appears to be initiated before the grid is rendered.
I solved my problem by creating my store with a memory proxy containing no data.
me.store = Ext.create('Ext.data.Store', {
model: 'SummaryData',
data: [],
proxy: {
type: 'memory',
reader: 'array'
},
remoteSort: true,
remoteFilter: true
});
Then set up an afterrender handler on the grid to poke in the correct proxy and initiate a load of the store.
afterrender: function () {
var me = this;
me.store.setProxy({
type: 'ajax',
url : '/print_unallocated/change_site__data',
reader: {
type: 'json',
root: 'rows'
},
listeners: {
exception: function (proxy, response) {
Max.reportException(response);
}
}
});
me.filters.createFilters();
me.store.load();
},
In the source, you can see a comment related to this.
// Call getMenu() to ensure the menu is created, and so, also are the filters. We cannot call
// createFilters() withouth having a menu because it will cause in a recursion to applyState()
// that ends up to clear all the filter values. This is likely to happen when we reorder a column
// and then add a new filter before the menu is recreated.
me.view.headerCt.getMenu();
You can test whether the menu has been created before applying your filter. If it hasn't, do it yourself.
if(!grid.getView().headerCt.menu){
grid.getView().headerCt.getMenu();
}
I write special combo object to use it as linked combos. Here it is:
comboDivClass = Ext.extend(Ext.form.ComboBox, {
fieldLabel: 'Divisions',
anchor: '95%',
lazyRender:true,
store:new Ext.data.Store({
proxy: proxy,
baseParams:{rfb_type:'divisions'},
reader: divReader,
autoLoad: true
}),
displayField:'div_name',
allowBlank:false,
valueField:'div_id',
triggerAction:'all',
mode:'local',
listeners:{
select:{
fn:function(combo, value) {
if (this.idChildCombo) {
var modelCmp = Ext.getCmp(this.idChildCombo);
modelCmp.setValue('');
modelCmp.getStore().reload({
params: { 'div_id': this.getValue() }
});
}
}
}
},/**/
hiddenName:'div_id',
initComponent: function() {comboDivClass.superclass.initComponent.call(this);}})
As you may see, this combobox load data at child combobox store(which set as idChildCombo).
Ok. Here is how i declare it
new comboDivClass({id:'sub0div',idChildCombo:'sub1div'}),
new comboDivClass({id:'sub1div'})
Yes it works, but it have some odd trouble - it load not only sub1div store, it load at sub0div store too. Why? What im doing wrong?
One thing I see is that you have mode: 'local' config, while it should be remote.
Other thing to consider: Why don't you do it more like this:
var c1 = new comboDivClass({id:'sub0div'});
var c2 = new comboDivClass({id:'sub1div'});
c1.on('select',function(combo, value) {c2.getStore().reload({
params: { 'div_id': c1.getValue() }
})});
ChildCombo.setMasterField( masterField ) {
masterField.on('change', function(field){
this.getStore().filterBy( function(){ //filter by masterFIeld.getValue() } );
})
}
The idea is to link child field to parent not parent to child and this way you can link to any kind form field no only combo.
Here in child combo you have to have store with three columns [group,value,label] where group is value of master field.
This way you can manage to have mode than one master field.
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?