PDA

View Full Version : [FIXED][BUG][PR 3] Names of model associations automatic functions collide



kveeiv
10 Mar 2011, 7:05 AM
If you have an association in your data that has the same name as one of the members of the base record object, the naming will collide and the automatically generated functions will not be created, causing cascading failures. Example:


Ext.onReady(function() {

var failData = [{
id: 'parent1',
role: 'owner',
fields: [{
id: 'child1',
type: 'A'
},{
id: 'child2',
type: 'A'
}]
}];

Ext.regModel('TestChildren', {
idProperty: 'id',
fields: [
{name:'id', type:'string'},
{name:'type', type:'string'}
]
});

Ext.regModel('TestParent', {
idProperty: 'id',
fields: [{
name:'id', type:'string'
},{
name:'role', type:'string'
}],
hasMany: [{
model: 'TestChildren',
name: 'fields'
}]
});

var store = new Ext.data.Store({
model: 'TestParent',
autoLoad: true,
proxy: {
type: 'memory',
reader: {
type: 'json',
root: 'records'
}
},
data: {
records: failData
}
});

});

This data will not load because of the child being named "fields". It looks like the record[associationName]() function does not get created in this case, so when the readAssociated() function tries to retrieve the store for that association, it pukes (ext-all-debug.js:23123).

Changing the name of the field to something that doesn't collide avoids this issue. For example, in the test above replace 'fields' in the failData object and 'fields' in the hasMany definition for TestParent with 'children'. Sometimes, such as when loading data from a third-party service, we don't have control over the data structure that we are loading.

evant
14 Mar 2011, 1:40 AM
I don't really see what we can do here, the same could apply to numerous points in the library.



Ext.onReady(function(){
var ct = Ext.create('Ext.container.Container', {
render: true,
items: {
html: 'Foo'
}
});
ct.render(document.body); //explode
});


The case you've posted is more insidious because these properties/methods get created on the fly, but it's essentially the same concept.

kveeiv
14 Mar 2011, 6:38 AM
I hadn't really considered the parallels to the overriding functionality, probably as you said because the functions are created on the fly so its not as obvious that it would be happening, and also because (as was my case) these definitions were created dynamically based on 3rd party data. Its more obvious - and I think less likely - when an override is defined explicitly on a created object as it is in your example.

With consideration for that, perhaps the associations could have implemented the "mapping" configuration that is implemented for fields. This would provide a few benefits:



...
associations: [
// Specifically avoid particular association name collisions
{ type: 'hasMany', name: 'children', mapping: 'fields'},
// Model.fields() => Model.children()

// Make dumb source-data relationship names easier to use on the client side
{ type: 'hasMany', name: 'betterName', mapping: 'other_people_name_things_dumbly' },
// Model.other_people_name_things_dumbly() => Model.betterName()

// Or even (probably unwisely) hijack the cleverness of the store accessor name creation
{ type: 'hasMany', name: 'getFieldStore', mapping: 'fields' }
// Model.fields() => Model.getFieldStore()
]
...

evant
14 Mar 2011, 6:43 AM
Yeah, after I posted I was looking at a few other things in the data package and I had a similar thought, a mapping could be useful here.

evant
20 Mar 2011, 8:14 PM
Upon review this is already possible:



Ext.require('Ext.data.*');

Ext.onReady(function(){
Ext.onReady(function(){

var failData = [{
id: 'parent1',
role: 'owner',
fields: [{
id: 'child1',
type: 'A'
}, {
id: 'child2',
type: 'A'
}]
}];

Ext.regModel('TestChildren', {
idProperty: 'id',
fields: [{
name: 'id',
type: 'string'
}, {
name: 'type',
type: 'string'
}]
});

Ext.regModel('TestParent', {
idProperty: 'id',
fields: [{
name: 'id',
type: 'string'
}, {
name: 'role',
type: 'string'
}],
hasMany: [{
model: 'TestChildren',
name: 'children',
associationKey: 'fields'
}]
});

var store = new Ext.data.Store({
model: 'TestParent',
autoLoad: true,
proxy: {
type: 'memory',
reader: {
type: 'json',
root: 'records'
}
},
data: {
records: failData
}
});

console.log(store.first().children().getCount());

});
});