PDA

View Full Version : DataView doesn't load belongsTo associations, so they can't be used in List's itemTpl



kleinsch
22 Aug 2011, 11:33 AM
Sencha Touch version tested:

1.1.0


only default ext-all.css


Platform tested against:

Chrome
iOS4


Description:

DataView doesn't fetch belongsTo associations to use in item templates. There's code in DataView:prepareAssociatedData() that pulls back hasMany, but it ignores belongsTo, so those items can't be used when templating a list.


Test Case:




Ext.ns('MyApp');

MyApp.Product = Ext.regModel('Product', {
fields: [
{name: 'id', type: 'int'},
{name: 'user_id', type: 'int'},
{name: 'name', type: 'string'}
],


belongsTo: [
{model: 'User', name: 'user'}
],

proxy: {
type: 'memory',
id: 'user'
}
});


MyApp.User = Ext.regModel('User', {
fields: [
{name: 'id', type: 'int'},
{name: 'name', type: 'string'}
],


associations: [
{type: 'hasMany', model: 'Product', name: 'products'}
],

proxy: {
type: 'memory',
id: 'product'
}
});


Ext.regStore('User', {
model: "User",
data: [
{
id: 1,
name: "nick"
},
{
id: 2,
name: "jim"
}
]

});


Ext.regStore('Product', {
model: "Product",
data: [
{
id: 1,
user_id: 1,
name: "myProduct"
},
{
id: 2,
user_id: 1,
name: "myProduct2"
},
{
id: 3,
user_id: 2,
name: "myProduct3"
},
{
id: 4,
user_id: 2,
name: "myProduct4"
}
]
});


new Ext.Application({
launch: function() {
new Ext.Panel({
fullscreen: true,
items: [
{
id: 'productsList',
xtype: 'list',
store: Ext.getStore('Product'),
itemTpl: '{user.data.name} - {name}'
}
]
});
}
});



Steps to reproduce the problem:

Run provided test case


The result that was expected:

Should display list of four products, each with correct names


The result that occurs instead:

Javascript error prevents list from loading, because templating system complains that user.data is undefined.


Possible Fix:

Hacked this together to fix the problem temporarily, but it's probably not optimal, as I don't understand the underlying architecture completely.


Ext.DataView.override({
prepareAssociatedData: function(record, ids) {
//we keep track of all of the internalIds of the models that we have loaded so far in here
ids = ids || [];

var associations = record.associations.items,
associationCount = associations.length,
associationData = {},
associatedStore, associatedName, associatedRecords, associatedRecord,
associatedRecordCount, association, internalId, i, j;

for (i = 0; i < associationCount; i++) {
association = associations[i];


if (association.type == "belongsTo") {
var foreignKeyId = record.data[association.foreignKey];
var storeName = association.associatedName;
record.data[association.name] = Ext.getStore(storeName).getById(foreignKeyId);
} else {
//this is the hasMany store filled with the associated data
associatedStore = record[association.storeName];

//we will use this to contain each associated record's data
associationData[association.name] = [];

//if it's loaded, put it into the association data
if (associatedStore && associatedStore.data.length > 0) {
associatedRecords = associatedStore.data.items;
associatedRecordCount = associatedRecords.length;

//now we're finally iterating over the records in the association. We do this recursively
for (j = 0; j < associatedRecordCount; j++) {
associatedRecord = associatedRecords[j];
internalId = associatedRecord.internalId;

//when we load the associations for a specific model instance we add it to the set of loaded ids so that
//we don't load it twice. If we don't do this, we can fall into endless recursive loading failures.
if (ids.indexOf(internalId) == -1) {
ids.push(internalId);

associationData[association.name][j] = associatedRecord.data;
Ext.apply(associationData[association.name][j], this.prepareAssociatedData(associatedRecord, ids));
}
}
}
}
}

return associationData;
}
});

oddz
30 Sep 2011, 11:51 AM
It is difficult to believe this was over look, considering how important it is. I mean who doesn't manage belongs to associations. Anyway... I think the below is a more optimal fix considering the relational data is present one the record. The only difference it look like between hasMany and belongsTo is that the association data is stored in a property called modelnameBelongsToInstance. So there isn't an apparent need to reload data using the store. I might be wrong though... I'm merely going of of what I see when I log/inspect the record data with some peaking under the hood.



Ext.DataView.override({
prepareAssociatedData: function(record, ids) {
//we keep track of all of the internalIds of the models that we have loaded so far in here
ids = ids || [];

var associations = record.associations.items,
associationCount = associations.length,
associationData = {},
associatedStore, associatedName, associatedRecords, associatedRecord,
associatedRecordCount, association, internalId, i, j;

for (i = 0; i < associationCount; i++) {
association = associations[i];

if(association.type === 'belongsTo') {

associatedRecord = record[association.associatedName + 'BelongsToInstance'];

if(associatedRecord) {
associationData[association.name] = associatedRecord.data;
Ext.apply(associationData[association.name], this.prepareAssociatedData(associatedRecord, ids));
}

} else {

//this is the hasMany store filled with the associated data
associatedStore = record[association.storeName];

//we will use this to contain each associated record's data
associationData[association.name] = [];

//if it's loaded, put it into the association data
if (associatedStore && associatedStore.data.length > 0) {
associatedRecords = associatedStore.data.items;
associatedRecordCount = associatedRecords.length;

//now we're finally iterating over the records in the association. We do this recursively
for (j = 0; j < associatedRecordCount; j++) {
associatedRecord = associatedRecords[j];
internalId = associatedRecord.internalId;

//when we load the associations for a specific model instance we add it to the set of loaded ids so that
//we don't load it twice. If we don't do this, we can fall into endless recursive loading failures.
if (ids.indexOf(internalId) == -1) {
ids.push(internalId);

associationData[association.name][j] = associatedRecord.data;
Ext.apply(associationData[association.name][j], this.prepareAssociatedData(associatedRecord, ids));
}
}
}

}

}

return associationData;
}
});

Möhre
25 Oct 2011, 8:04 AM
Has this been fixed in 1.1.1 oder 2.0.0?

oddz
25 Oct 2011, 4:48 PM
It isn't really a bug more so a feature enhancement. None the less, it has not been added in 1.1 or 2.0. You can see if you go look at the code.

Möhre
25 Oct 2011, 11:28 PM
just another association bug:
http://www.sencha.com/forum/showthread.php?115383-Can-t-have-more-than-1-hasMany-association

I have the impression most devs are only progging tiny apps with sencha touch but no bigger databased ones with more than one table or relation...

Maybe I will give up and come back in a year or two when Touch 3.1 is ready for prime time;)