PDA

View Full Version : Linked combobox and model associations



jmozley
13 Apr 2012, 4:48 AM
I have two combo boxes I want to use to select a parent and child based on a relationship such as country and city within the country. I've done this kind of thing with ExtJS 3 but am trying to use the model associations of Ext 4. I have this working to the point where the child store is loaded, but the values are not displayed in the child combo. The view, controller, stores and models are below.

Any suggestions on what I am doing wrong so that the combo does not display the store contents? The associations and loading of the store seems to work.

View:


Ext.define('MyApp.view.FormOne', {
extend: 'MyApp.view.BaseForm',

alias : 'widget.form-one',

title : 'Form One',

url : 'cgi-bin/create_me_for_form_one.pl',

initComponent: function () {

Ext.apply(this, {

items : [
{
xtype : 'combobox',
itemId : 'countryCombo',
name : 'country',
fieldLabel : 'Country',
emptyText : 'Select a country...',
displayField : 'country_name',
valueField : 'country_code',
store : 'CountryStore',
mode : 'local',
triggerAction : 'all',
remoteSort : true,
lastQuery : ''
},
{
xtype : 'combobox',
itemId : 'cityCombo',
disabled : true,
name : 'city',
fieldLabel : 'City',
displayField : 'city_name',
valueField : 'city_code',
store : 'CityStore',
mode : 'local',
triggerAction : 'all',
remoteSort : true,
lastQuery : ''
}
]
});

this.callParent(arguments);
}

});


Controller:


Ext.define('MyApp.controller.FormOneController', {
extend: 'MyApp.controller.BaseFormController',

formSelector: 'form-one',

views: [
'FormOne'
],

stores: [
'CountryStore',
'CityStore'
],

models: [
'CountryModel',
'CityModel'
],

refs: [
{
ref : 'formOne',
selector : 'formOne',
xtype : 'form-one',
autoCreate : true
}
],

init : function (application) {
if (this.inited) {
return;
}
this.inited = true;


this.control({

'.form-one #countryCombo' : {
select : this.onSelectCountry
}
});

this.callParent([application]);
},


actionDisplayView : function () {
//this.application.logIfConsole('function DisplayView');
this.application.setMainView(this.getFormOne());
},

onSelectCountry : function (countryCombo, countryArray) {

// Get parent form and enable the city combo within this
var parentForm = countryCombo.findParentByType('form');
var cityCombo = parentForm.child('#cityCombo');
cityCombo.setDisabled(false);

// load city store
//countryArray[0].cities().load();
countryArray[0].cities().load({
callback: function(city, operation) {
console.log(arguments);
Ext.each(city, function(city) {
console.log(city);
// This shows child records in firebug when a country is selected
});
}
});

// Missing something to get the combo to display to loaded store?

}

});


Country model:


Ext.define('MyApp.model.CountryModel', {
extend : 'Ext.data.Model',

idProperty : 'uid_country',

fields : [
'uid_country',
'country_name',
'country_code'
],

proxy: {
type : 'ajax',
url : 'data/countries.json',
reader : {
type : 'json',
root : 'data'
}
},

hasMany: {
model : 'MyApp.model.CityModel',
name : 'cities',
primaryKey : 'uid_country',
foreignKey : 'fk_t_country_uid_country'
}

});


Country store:


Ext.define('MyApp.store.CountryStore', {
extend : 'Ext.data.Store',

storeId : 'Country',
requires : 'MyApp.model.CountryModel',
model : 'MyApp.model.CountryModel',
root : 'data',
autoLoad : true

});


City model:


Ext.define('MyApp.model.CityModel', {
extend : 'Ext.data.Model',

idProperty : 'uid_city',

fields : [
'uid_city',
'city_name',
'city_code',
'fk_t_country_uid_country'
],

proxy: {
type : 'ajax',
url : 'data/cities.json',
reader : {
type : 'json',
root : 'data'
}
},

associations: [
{
type : 'belongsTo',
model : 'MyApp.model.CountryModel',
name : 'country',
primaryKey : 'uid_country',
foreignKey : 'fk_t_country_uid_country'
}
]

});


City store:


Ext.define('MyApp.store.CityStore', {
extend : 'Ext.data.Store',

storeId : 'City',
model : 'MyApp.model.CityModel',
root : 'data',

autoLoad: false
});

mitchellsimoens
13 Apr 2012, 8:03 AM
When the parent combobox is selected, you need to execute the getter method to get the cities for the child combobox which you are doing to load the store. However that store instance isn't on the child combobox, a different store instance is and you need to set the store onto the child combobox.

jmozley
13 Apr 2012, 9:07 AM
Hi Mitchell,

So if I understand correctly by using countryArray[0].cities().load() I am actually creating a new instance of the store, not updating the one associated with the cities combobox created though it's configuration?

So therefore I have to dynamically change the city combobox to replace the store it was instantiated with by the one I've just created using cities().load() ?

mitchellsimoens
13 Apr 2012, 9:37 AM
When you do


countryArray[0].cities()

That is returning a store instance. Each record in the country store will have a separate store instance when you execute cities()

jmozley
13 Apr 2012, 10:00 AM
Nope I'm still not following. I can now see that:



countryArray[0].cities()


returns one store instance (with the correct child records via the model proxy).

However, a combo (I thought) was designed to be "hardcoded" to a store. So I cannot replace that store with another, or at least that's what I thought. So would I load the store bound to the combo with the records in the returned store instance?

In the past I've reloaded the child combo with parameter(s) to get the children. I can follow this approach if that is the best way to deal with this. I don't know that I'm saving anything by creating a new store and I cannot see an obvious way to replace a combo's store. I had thought that the new data model and association might save me some work here, but I guess that either I don't understand or I'm approaching this the worng way.

mitchellsimoens
13 Apr 2012, 10:27 AM
execute bindStore passing in the new store.

jmozley
13 Apr 2012, 10:44 PM
The following does not work:



countryCombo.bindStore( countryArray[0].cities() );


I did not see bindStore as a possible solution as it is not in the ExtJS docs as a method under Ext.form.field.Combobox.

After this I also tried loading the store, but this ends up just calling the CGI script to load all data (without a filter) presumably via the models API.

I cannot get away from the thought that I am approaching this the wrong way. Maybe I should apply a filter to the city store on selection of the country store and then load the combo?

The examples I've found of using the model relationships do not include combos, so maybe they are not suited/designed for this purpose?

jmozley
14 Apr 2012, 12:28 AM
Hi Mitchell,

This does work:



onSelectCountry : function (countryCombo, countryArray) {

var parentForm = countryCombo.findParentByType('form');
var cityCombo = parentForm.child('#cityCombo');
cityCombo.setDisabled(false);
var cityStore = cityCombo.getStore();
cityStore.removeAll();
countryArray[0].cities().load({
callback: function(city, operation) {
Ext.each(city, function(city) {
cityStore.add(city);
});
}
});

}


Does this seem reasonable?

The problem is I don't see that the model relationship has added much of benefit. I could have got the unique ID of the parent (country) and loaded the child by passing the ID as a parameter.

If the city data was local I could see the point, I could filter the data or maybe the model relationship would automate this. If the data is remote, so I use an AJAX proxy, then I have not reduced the number of remote calls. I have gained the benefit of not needing to know and deal with the keys in the backend database as I defined the relationship in the model, so this may be of benfit if I use the relationship in a number of places.

As a slight aside (but what got me onto this problem in the first place) was replacing this kind of thing I would have used in ExtJS 3:



cityCombo.store.reload({
params: { fk_country_id : countryCombo.getValue() }
});


I didn't see an equivalent of this, hence exploring the model relationships.

What I'm looking for is the typical way linked combos would be implemented in ExtJS 4. Whether using model relationships to mirror database relationships is a good approach for instance.

PKrishna
27 Nov 2013, 6:46 AM
Actually a have same problem. If you overcome this problem, can u send me the code on my personal mail id(durgapujakol@gmail.com) or update here...