PDA

View Full Version : Cannot extend a Model extension



Stokes
20 Apr 2012, 3:02 PM
In several ways, the way Ext.data.Model instances are constructed causes problems. One of them is extending an extension. I can create a subclass of Ext.data.Model, but when I create a subclass of that subclass (which I would expect to be pretty common in OO code using an MVC pattern) bad stuff starts to happen.

The code below demonstrates the problem I'm seeing. We just want to create a Person class, and then have some specialized subclass (NicePerson in this example, I probably could have thought of something better). The Ext.define() of the subclass causes the original Person class to be corrupted.


// Create a model Person, which has a 'child' model Address using a HasMany association
Ext.define('Address', {
extend: 'Ext.data.Model',
fields: [
{name: 'street1', type: 'string'},
{name: 'street1', type: 'string'},
{name: 'city', type: 'string'},
{name: 'state', type: 'string'},
{name: 'zip', type: 'string'}
]
});
Ext.define('Person', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'firstName', type: 'string'},
{name: 'lastName', type: 'string'}
],
hasMany: [
{model: 'Address', associationKey: 'addresses', name: 'getAddressStore'}
]
});
// create a Store of Person objects, and it works just fine.
var somePeople = Ext.create('Ext.data.Store', {
model: 'Person',
proxy: { type: 'memory', reader: { type: 'json'}},
data: [
{id: 1, firstName: 'Brian', lastName: 'M', addresses: [{street1: '1234 My Street', city: 'Austin', state: 'TX', zip: '77777'}]},
{id: 2, firstName: 'Miro', lastName: 'P', addresses: [{street1: '1234 Breakaway', city: 'Cedar Park', state: 'TX', zip: '77777'}]},
{id: 3, firstName: 'Stokes', lastName: 'J', addresses: [{street1: '1234 Vance', city: 'Jonestown', state: 'TX', zip: '77777'}]}
]
});
// I can even do that again, and create another store with very similar people.
var someMorePeople = Ext.create('Ext.data.Store', {
model: 'Person',
proxy: { type: 'memory', reader: { type: 'json'}},
data: [
{id: 1, firstName: 'Brian', lastName: 'M', addresses: [{street1: '1234 My Street', city: 'Austin', state: 'TX', zip: '77777'}]},
{id: 2, firstName: 'Miro', lastName: 'P', addresses: [{street1: '1234 Breakaway', city: 'Cedar Park', state: 'TX', zip: '77777'}]},
{id: 3, firstName: 'Stokes', lastName: 'J', addresses: [{street1: '1234 Vance', city: 'Jonestown', state: 'TX', zip: '77777'}]}
]
});
// Now I create a subclass of Person. It doesn't matter (in this case) whether the subclass defines associations or not, or
// even whether it defines fields, just extending the Person class causes bad stuff to happen. The Person class appears
// to be corrupt after this.
Ext.define('NicePerson', {
extend: 'Person',
fields: [
{name: 'id', type: 'int'},
{name: 'firstName', type: 'string'},
{name: 'lastName', type: 'string'},
{name: 'phoneNumber', type: 'string'}
]
});
// This demonstrates the corruption. Trying to create the same store that we did twice above now throws, in ExtJS 4.1.0rc3:
// TypeError: Cannot read property 'observable' of undefined
// at new <anonymous> (ext-all-debug-w-comments.js:57742:18)
// at [object Object].readAssociated (ext-all-debug-w-comments.js:57950:34)
// at [object Object].<anonymous> (ext-all-debug-w-comments.js:57917:20)
// at [object Object].<anonymous> (ext-all-debug-w-comments.js:6084:32)
// at [object Object].extractData (ext-all-debug-w-comments.js:58517:21)
// at [object Object].<anonymous> (ext-all-debug-w-comments.js:57870:30)
// at [object Object].<anonymous> (ext-all-debug-w-comments.js:6084:32)
// at [object Object].readRecords (ext-all-debug-w-comments.js:58449:21)
// at [object Object].read (ext-all-debug-w-comments.js:57795:82)
// at [object Object].read (ext-all-debug-w-comments.js:56075:29)

var theSamePeople = Ext.create('Ext.data.Store', {
model: 'Person',
proxy: { type: 'memory', reader: { type: 'json'}},
data: [
{id: 1, firstName: 'Brian', lastName: 'M', addresses: [{street1: '1234 My Street', city: 'Austin', state: 'TX', zip: '77777'}]},
{id: 2, firstName: 'Miro', lastName: 'P', addresses: [{street1: '1234 Breakaway', city: 'Cedar Park', state: 'TX', zip: '77777'}]},
{id: 3, firstName: 'Stokes', lastName: 'J', addresses: [{street1: '1234 Vance', city: 'Jonestown', state: 'TX', zip: '77777'}]}
]
});

Any ideas how to work around this? (Or am I misunderstanding something?)

Stokes.

vietits
20 Apr 2012, 8:33 PM
I think it's a bug because when you define NicePerson class (a child class of Person) it causes the associations property of Person class (parent class) to be changed. This is a problem of using shared objects.

This is from Ext.data.Model (Ext 4.0.7 and Ext 4.1.):


...
Ext.require(dependencies, function() {
Ext.ModelManager.registerType(name, cls);

for (i = 0, ln = associations.length; i < ln; ++i) {
association = associations[i]; <- assocation is a reference to object in parent class

Ext.apply(association, { //<- cause parent class changed
ownerModel: name,
associatedModel: association.model
});


Below is my suggestion to fix this problem:
1. Define FixedModel (or anyname you like):


Ext.define('FixedModel',{
extend: 'Ext.data.Model',

onClassExtended: function(cls, data) {
// var onBeforeClassCreated = data.onBeforeClassCreated;
var onBeforeClassCreated = function(cls, data) {
onClassCreated = data.onClassCreated;
delete data.onBeforeClassCreated;
delete data.onClassCreated;
cls.implement(data);
onClassCreated.call(cls, cls);
};


data.onBeforeClassCreated = function(cls, data) {
var me = this,
name = Ext.getClassName(cls),
prototype = cls.prototype,
superCls = cls.prototype.superclass,

validations = data.validations || [],
fields = data.fields || [],
associations = data.associations || [],
belongsTo = data.belongsTo,
hasMany = data.hasMany,
idgen = data.idgen,

fieldsMixedCollection = new Ext.util.MixedCollection(false, function(field) {
return field.name;
}),

associationsMixedCollection = new Ext.util.MixedCollection(false, function(association) {
return association.name;
}),

superValidations = superCls.validations,
superFields = superCls.fields,
superAssociations = superCls.associations,

association, i, ln,
dependencies = [];

// Save modelName on class and its prototype
cls.modelName = name;
prototype.modelName = name;

// Merge the validations of the superclass and the new subclass
if (superValidations) {
validations = superValidations.concat(validations);
}

data.validations = validations;

// Merge the fields of the superclass and the new subclass
if (superFields) {
fields = superFields.items.concat(fields);
}

for (i = 0, ln = fields.length; i < ln; ++i) {
fieldsMixedCollection.add(new Ext.data.Field(fields[i]));
}

data.fields = fieldsMixedCollection;

if (idgen) {
data.idgen = Ext.data.IdGenerator.get(idgen);
}

//associations can be specified in the more convenient format (e.g. not inside an 'associations' array).
//we support that here
if (belongsTo) {
belongsTo = Ext.Array.from(belongsTo);

for (i = 0, ln = belongsTo.length; i < ln; ++i) {
association = belongsTo[i];

if (!Ext.isObject(association)) {
association = {model: association};
}

association.type = 'belongsTo';
associations.push(association);
}

delete data.belongsTo;
}

if (hasMany) {
hasMany = Ext.Array.from(hasMany);
for (i = 0, ln = hasMany.length; i < ln; ++i) {
association = hasMany[i];

if (!Ext.isObject(association)) {
association = {model: association};
}

association.type = 'hasMany';
associations.push(association);
}

delete data.hasMany;
}

if (superAssociations) {
associations = superAssociations.items.concat(associations);
}

for (i = 0, ln = associations.length; i < ln; ++i) {
dependencies.push('association.' + associations[i].type.toLowerCase());
}

if (data.proxy) {
if (typeof data.proxy === 'string') {
dependencies.push('proxy.' + data.proxy);
}
else if (typeof data.proxy.type === 'string') {
dependencies.push('proxy.' + data.proxy.type);
}
}

Ext.require(dependencies, function() {
Ext.ModelManager.registerType(name, cls);

for (i = 0, ln = associations.length; i < ln; ++i) {
// association = associations[i];
association = Ext.apply({}, associations[i]); // <- Create assocaition as new object. Logically, we can use Ext.clone() but in fact it has no effect.

Ext.apply(association, {
ownerModel: name,
associatedModel: association.model
});

if (Ext.ModelManager.getModel(association.model) === undefined) {
Ext.ModelManager.registerDeferredAssociation(association);
} else {
associationsMixedCollection.add(Ext.data.Association.create(association));
}
}
data.associations = associationsMixedCollection;

onBeforeClassCreated.call(me, cls, data);

cls.setProxy(cls.prototype.proxy || cls.prototype.defaultProxyType);

// Fire the onModelDefined template method on ModelManager
Ext.ModelManager.onModelDefined(cls);
});
};
}
});

2. Define your models by extending them from FixedModel instead of Ext.data.Model


Ext.define('Address', {
extend: 'FixedModel', // here you can use Ext.data.Model
fields: [
{name: 'street1', type: 'string'},
{name: 'street1', type: 'string'},
{name: 'city', type: 'string'},
{name: 'state', type: 'string'},
{name: 'zip', type: 'string'}
]
});
Ext.define('Person', {
extend: 'FixedModel',
fields: [
{name: 'id', type: 'int'},
{name: 'firstName', type: 'string'},
{name: 'lastName', type: 'string'}
],
hasMany: [
{model: 'Address', associationKey: 'addresses', name: 'getAddressStore'}
]
});
Ext.define('NicePerson', {
extend: 'Person',
fields: [
{name: 'id', type: 'int'},
{name: 'firstName', type: 'string'},
{name: 'lastName', type: 'string'},
{name: 'phoneNumber', type: 'string'}
]
});


var theSamePeople = Ext.create('Ext.data.Store', {
model: 'Person',
proxy: { type: 'memory', reader: { type: 'json'}},
data: [
{id: 1, firstName: 'Brian', lastName: 'M', addresses: [{street1: '1234 My Street', city: 'Austin', state: 'TX', zip: '77777'}]},
{id: 2, firstName: 'Miro', lastName: 'P', addresses: [{street1: '1234 Breakaway', city: 'Cedar Park', state: 'TX', zip: '77777'}]},
{id: 3, firstName: 'Stokes', lastName: 'J', addresses: [{street1: '1234 Vance', city: 'Jonestown', state: 'TX', zip: '77777'}]}
]
});

Stokes
23 Apr 2012, 9:02 AM
Vietits,

Thanks! That got me closer (I think), but I still get an error, this time when it's creating the Reader of the first Store. I've seen this kind of thing before too, but don't understand what causes it.


TypeError: Cannot read property 'observable' of undefined
at new <anonymous> (ext-all-debug-w-comments.js:57742:18)
at [object Object].readAssociated (ext-all-debug-w-comments.js:57950:34)
at [object Object].<anonymous> (ext-all-debug-w-comments.js:57917:20)
at [object Object].<anonymous> (ext-all-debug-w-comments.js:6084:32)
at [object Object].extractData (ext-all-debug-w-comments.js:58517:21)
at [object Object].<anonymous> (ext-all-debug-w-comments.js:57870:30)
at [object Object].<anonymous> (ext-all-debug-w-comments.js:6084:32)
at [object Object].readRecords (ext-all-debug-w-comments.js:58449:21)
at [object Object].read (ext-all-debug-w-comments.js:57795:82)
at [object Object].read (ext-all-debug-w-comments.js:56075:29)


Stokes.

Stokes
23 Apr 2012, 9:28 AM
Actually, I spoke too soon. When I looked more carefully, your version of Ext.data.Model is different than the one I have in 4.1rc3. So I applied your change to my Ext.data.Model extension as follows:

Ext.define('FixedModel',{
extend: 'Ext.data.Model',
requires: [
'Ext.util.Observable'
],
onClassExtended: function(cls, data, hooks) {
var onBeforeClassCreated = hooks.onBeforeCreated;

hooks.onBeforeCreated = function(cls, data) {
var me = this,
name = Ext.getClassName(cls),
prototype = cls.prototype,
superCls = cls.prototype.superclass,

validations = data.validations || [],
fields = data.fields || [],
associations = data.associations || [],
belongsTo = data.belongsTo,
hasMany = data.hasMany,
hasOne = data.hasOne,
addAssociations = function(items, type) {
var i = 0,
len,
item;

if (items) {
items = Ext.Array.from(items);

for (len = items.length; i < len; ++i) {
item = items[i];

if (!Ext.isObject(item)) {
item = {model: item};
}

item.type = type;
associations.push(item);
}
}
},
idgen = data.idgen,

fieldsMixedCollection = new Ext.util.MixedCollection(false, prototype.itemNameFn),

associationsMixedCollection = new Ext.util.MixedCollection(false, prototype.itemNameFn),

superValidations = superCls.validations,
superFields = superCls.fields,
superAssociations = superCls.associations,

association, i, ln,
dependencies = [],
idProperty = data.idProperty || cls.prototype.idProperty,
fieldConvertSortFn = Ext.Function.bind(
fieldsMixedCollection.sortBy,
fieldsMixedCollection,
[prototype.sortConvertFields], false),

// Use the proxy from the class definition object if present, otherwise fall back to the inherited one, or the default
clsProxy = data.proxy || cls.prototype.proxy || cls.prototype.defaultProxyType;

// Save modelName on class and its prototype
cls.modelName = name;
prototype.modelName = name;

// Merge the validations of the superclass and the new subclass
if (superValidations) {
validations = superValidations.concat(validations);
}

data.validations = validations;

// Merge the fields of the superclass and the new subclass
if (superFields) {
fields = superFields.items.concat(fields);
}

fieldsMixedCollection.on({
add: fieldConvertSortFn,
replace: fieldConvertSortFn
});

for (i = 0, ln = fields.length; i < ln; ++i) {
fieldsMixedCollection.add(new Ext.data.Field(fields[i]));
}
if (!fieldsMixedCollection.get(idProperty)) {
fieldsMixedCollection.add(new Ext.data.Field(idProperty));
}
data.fields = fieldsMixedCollection;

if (idgen) {
data.idgen = Ext.data.IdGenerator.get(idgen);
}

//associations can be specified in the more convenient format (e.g. not inside an 'associations' array).
//we support that here
addAssociations(data.belongsTo, 'belongsTo');
delete data.belongsTo;
addAssociations(data.hasMany, 'hasMany');
delete data.hasMany;
addAssociations(data.hasOne, 'hasOne');
delete data.hasOne;

if (superAssociations) {
associations = superAssociations.items.concat(associations);
}

for (i = 0, ln = associations.length; i < ln; ++i) {
dependencies.push('association.' + associations[i].type.toLowerCase());
}

// If we have not been supplied with a Proxy *instance*, then add the proxy type to our dependency list
if (clsProxy && !clsProxy.isProxy) {
dependencies.push('proxy.' + (typeof clsProxy === 'string' ? clsProxy : clsProxy.type));
}

Ext.require(dependencies, function() {
Ext.ModelManager.registerType(name, cls);

for (i = 0, ln = associations.length; i < ln; ++i) {
// BEGIN: override
//association = associations[i];
association = Ext.apply({}, associations[i]); // <- Create assocaition as new object. Logically, we can use Ext.clone() but in fact it has no effect.
// END: override

Ext.apply(association, {
ownerModel: name,
associatedModel: association.model
});

if (Ext.ModelManager.getModel(association.model) === undefined) {
Ext.ModelManager.registerDeferredAssociation(association);
} else {
associationsMixedCollection.add(Ext.data.association.Association.create(association));
}
}

data.associations = associationsMixedCollection;

// onBeforeCreated may get called *asynchronously* if any of those required classes caused
// an asynchronous script load. This would mean that the class definition object
// has not been applied to the prototype when the Model definition has returned.
// The Reader constructor does not attempt to buildExtractors if the fields MixedCollection
// has not yet been set. The cls.setProxy call triggers a build of extractor methods.
onBeforeClassCreated.call(me, cls, data, hooks);

cls.setProxy(clsProxy);

// Fire the onModelDefined template method on ModelManager
Ext.ModelManager.onModelDefined(cls);
});
};
}
});

But I still get an error.

TypeError: Cannot call method 'toLowerCase' of undefined
at Function.<anonymous> (ext-all-debug-w-comments.js:69715:73)
at FixedModel.js:145:38
at [object Object].require (ext-all-debug-w-comments.js:9391:26)
at Object.require (ext-all-debug-w-comments.js:2926:39)
at Function.<anonymous> (FixedModel.js:117:17)
at Function.doProcess (ext-all-debug-w-comments.js:6501:39)
at Function.doProcess (ext-all-debug-w-comments.js:6506:20)
at ext-all-debug-w-comments.js:9727:24
at [object Object].require (ext-all-debug-w-comments.js:9391:26)
at Function.<anonymous> (ext-all-debug-w-comments.js:9694:16)


It's failing in the Ext.define() of my first subclass of FixedModel, but on this line in Ext.data.Model.onClassExtended().

for (i = 0, ln = associations.length; i < ln; ++i) {
dependencies.push('association.' + associations[i].type.toLowerCase());
}
Was the intent for both onClassExtended() functions to get called?

Thanks,
Stokes.

vietits
23 Apr 2012, 5:09 PM
My previous fix is for Ext 4.0.7. Below is my fix for Ext 4.1.0-rc3.


Ext.define('FixedModel', {
extend: 'Ext.data.Model',


onClassExtended: function(cls, data, hooks) {
// var onBeforeClassCreated = hooks.onBeforeCreated;
var onBeforeClassCreated = function(Class, data, hooks){
Class.addMembers(data);
hooks.onCreated.call(Class, Class);
}


hooks.onBeforeCreated = function(cls, data) {
var me = this,
name = Ext.getClassName(cls),
prototype = cls.prototype,
superCls = cls.prototype.superclass,


validations = data.validations || [],
fields = data.fields || [],
associations = data.associations || [],
belongsTo = data.belongsTo,
hasMany = data.hasMany,
hasOne = data.hasOne,
addAssociations = function(items, type) {
var i = 0,
len,
item;


if (items) {
items = Ext.Array.from(items);


for (len = items.length; i < len; ++i) {
item = items[i];


if (!Ext.isObject(item)) {
item = {model: item};
}


item.type = type;
associations.push(item);
}
}
},
idgen = data.idgen,


fieldsMixedCollection = new Ext.util.MixedCollection(false, prototype.itemNameFn),


associationsMixedCollection = new Ext.util.MixedCollection(false, prototype.itemNameFn),


superValidations = superCls.validations,
superFields = superCls.fields,
superAssociations = superCls.associations,


association, i, ln,
dependencies = [],
idProperty = data.idProperty || cls.prototype.idProperty,
fieldConvertSortFn = Ext.Function.bind(
fieldsMixedCollection.sortBy,
fieldsMixedCollection,
[prototype.sortConvertFields], false),


// Use the proxy from the class definition object if present, otherwise fall back to the inherited one, or the default
clsProxy = data.proxy || cls.prototype.proxy || cls.prototype.defaultProxyType;


// Save modelName on class and its prototype
cls.modelName = name;
prototype.modelName = name;


// Merge the validations of the superclass and the new subclass
if (superValidations) {
validations = superValidations.concat(validations);
}


data.validations = validations;


// Merge the fields of the superclass and the new subclass
if (superFields) {
fields = superFields.items.concat(fields);
}


fieldsMixedCollection.on({
add: fieldConvertSortFn,
replace: fieldConvertSortFn
});


for (i = 0, ln = fields.length; i < ln; ++i) {
fieldsMixedCollection.add(new Ext.data.Field(fields[i]));
}
if (!fieldsMixedCollection.get(idProperty)) {
fieldsMixedCollection.add(new Ext.data.Field(idProperty));
}
data.fields = fieldsMixedCollection;


if (idgen) {
data.idgen = Ext.data.IdGenerator.get(idgen);
}


//associations can be specified in the more convenient format (e.g. not inside an 'associations' array).
//we support that here
addAssociations(data.belongsTo, 'belongsTo');
delete data.belongsTo;
addAssociations(data.hasMany, 'hasMany');
delete data.hasMany;
addAssociations(data.hasOne, 'hasOne');
delete data.hasOne;


if (superAssociations) {
associations = superAssociations.items.concat(associations);
}


for (i = 0, ln = associations.length; i < ln; ++i) {
dependencies.push('association.' + associations[i].type.toLowerCase());
}


// If we have not been supplied with a Proxy *instance*, then add the proxy type to our dependency list
if (clsProxy && !clsProxy.isProxy) {
dependencies.push('proxy.' + (typeof clsProxy === 'string' ? clsProxy : clsProxy.type));
}


Ext.require(dependencies, function() {
Ext.ModelManager.registerType(name, cls);


for (i = 0, ln = associations.length; i < ln; ++i) { // association = associations[i];

association = Ext.apply({},associations[i]);



Ext.apply(association, {
ownerModel: name,
associatedModel: association.model
});


if (Ext.ModelManager.getModel(association.model) === undefined) {
Ext.ModelManager.registerDeferredAssociation(association);
} else {
associationsMixedCollection.add(Ext.data.association.Association.create(association));
}
}


data.associations = associationsMixedCollection;


// onBeforeCreated may get called *asynchronously* if any of those required classes caused
// an asynchronous script load. This would mean that the class definition object
// has not been applied to the prototype when the Model definition has returned.
// The Reader constructor does not attempt to buildExtractors if the fields MixedCollection
// has not yet been set. The cls.setProxy call triggers a build of extractor methods.
onBeforeClassCreated.call(me, cls, data, hooks);

cls.setProxy(clsProxy);


// Fire the onModelDefined template method on ModelManager
Ext.ModelManager.onModelDefined(cls);
});
};
}
});

BTW: I have posted this bug at http://www.sencha.com/forum/showthread.php?197810-Problem-in-extending-from-a-model-with-associations

Stokes
23 Apr 2012, 5:41 PM
Works perfectly for me. Thanks!

oilid
26 Apr 2012, 10:16 PM
Hey vietits,

is there maybe a solution/workaround for 4.1.0 final? I couldn't adapt your solution for Ext 4.1.0-rc3 to work with the 4.1.0 final release :-(

Thanks and regards,
Oilid

vietits
26 Apr 2012, 11:22 PM
Try this. Hope that it will solve your problem.


Ext.define('FixedModel', {
extend: 'Ext.data.Model',


onClassExtended: function(cls, data, hooks) {
var onBeforeClassCreated = function(Class, data, hooks){
Class.addMembers(data);
hooks.onCreated.call(Class, Class);
};
hooks.onBeforeCreated = function(cls, data) {
var me = this,
name = Ext.getClassName(cls),
prototype = cls.prototype,
superCls = cls.prototype.superclass,


validations = data.validations || [],
fields = data.fields || [],
associations = data.associations || [],
addAssociations = function(items, type) {
var i = 0,
len,
item;


if (items) {
items = Ext.Array.from(items);


for (len = items.length; i < len; ++i) {
item = items[i];


if (!Ext.isObject(item)) {
item = {model: item};
}


item.type = type;
associations.push(item);
}
}
},
idgen = data.idgen,


fieldsMixedCollection = new Ext.util.MixedCollection(false, prototype.itemNameFn),


associationsMixedCollection = new Ext.util.MixedCollection(false, prototype.itemNameFn),


superValidations = superCls.validations,
superFields = superCls.fields,
superAssociations = superCls.associations,


association, i, ln,
dependencies = [],
idProperty = data.idProperty || cls.prototype.idProperty,


// Process each Field upon add into the collection
onFieldAddReplace = function(arg0, arg1, arg2) {
var newField,
pos;


if (fieldsMixedCollection.events.add.firing) {
// Add event signature is (position, value, key);
pos = arg0;
newField = arg1;
} else {
// Replace event signature is (key, oldValue, newValue);
newField = arg2;
pos = arg1.originalIndex;
}


// Set the originalIndex for ArrayReader to get the default mapping from in case
// compareConvertFields changes the order due to some fields having custom convert functions.
newField.originalIndex = pos;


// The field(s) which encapsulates the idProperty must never have a default value set
// if no value arrives from the server side. So override any possible prototype-provided
// defaultValue with undefined which will inhibit generation of defaulting code in Reader.buildRecordDataExtractor
if (newField.mapping === idProperty || (newField.mapping == null && newField.name === idProperty)) {
newField.defaultValue = undefined;
}
},


// Use the proxy from the class definition object if present, otherwise fall back to the inherited one, or the default
clsProxy = data.proxy || cls.prototype.proxy || cls.prototype.defaultProxyType,


// Sort upon add function to be used in case of dynamically added Fields
fieldConvertSortFn = function() {
fieldsMixedCollection.sortBy(prototype.compareConvertFields);
};


// Save modelName on class and its prototype
cls.modelName = name;
prototype.modelName = name;


// Merge the validations of the superclass and the new subclass
if (superValidations) {
validations = superValidations.concat(validations);
}


data.validations = validations;


// Merge the fields of the superclass and the new subclass
if (superFields) {
fields = superFields.items.concat(fields);
}


fieldsMixedCollection.on({
add: onFieldAddReplace,
replace: onFieldAddReplace
});


for (i = 0, ln = fields.length; i < ln; ++i) {
fieldsMixedCollection.add(new Ext.data.Field(fields[i]));
}
if (!fieldsMixedCollection.get(idProperty)) {
fieldsMixedCollection.add(new Ext.data.Field(idProperty));
}


// Ensure the Fields are on correct order: Fields with custom convert function last
fieldConvertSortFn();
fieldsMixedCollection.on({
add: fieldConvertSortFn,
replace: fieldConvertSortFn
});


data.fields = fieldsMixedCollection;


if (idgen) {
data.idgen = Ext.data.IdGenerator.get(idgen);
}


//associations can be specified in the more convenient format (e.g. not inside an 'associations' array).
//we support that here
addAssociations(data.belongsTo, 'belongsTo');
delete data.belongsTo;
addAssociations(data.hasMany, 'hasMany');
delete data.hasMany;
addAssociations(data.hasOne, 'hasOne');
delete data.hasOne;


if (superAssociations) {
associations = superAssociations.items.concat(associations);
}


for (i = 0, ln = associations.length; i < ln; ++i) {
dependencies.push('association.' + associations[i].type.toLowerCase());
}


// If we have not been supplied with a Proxy *instance*, then add the proxy type to our dependency list
if (clsProxy && !clsProxy.isProxy) {
dependencies.push('proxy.' + (typeof clsProxy === 'string' ? clsProxy : clsProxy.type));
}


Ext.require(dependencies, function() {
Ext.ModelManager.registerType(name, cls);


for (i = 0, ln = associations.length; i < ln; ++i) {
association = Ext.apply({}, associations[i]);


Ext.apply(association, {
ownerModel: name,
associatedModel: association.model
});


if (Ext.ModelManager.getModel(association.model) === undefined) {
Ext.ModelManager.registerDeferredAssociation(association);
} else {
associationsMixedCollection.add(Ext.data.association.Association.create(association));
}
}


data.associations = associationsMixedCollection;


// onBeforeCreated may get called *asynchronously* if any of those required classes caused
// an asynchronous script load. This would mean that the class definition object
// has not been applied to the prototype when the Model definition has returned.
// The Reader constructor does not attempt to buildExtractors if the fields MixedCollection
// has not yet been set. The cls.setProxy call triggers a build of extractor methods.
onBeforeClassCreated.call(me, cls, data, hooks);


cls.setProxy(clsProxy);


// Fire the onModelDefined template method on ModelManager
Ext.ModelManager.onModelDefined(cls);
});
};
}
});

oilid
27 Apr 2012, 2:34 AM
Hey vietits,

thanks. This works for the associations. But I have a similiar bug/problem with the convert-function from Ext.data.Field:


Uncaught TypeError: Object #<Object> has no method 'convert'
Ext.define.constructor ext-all-debug.js:37449
constructor ext-all-debug.js:3892
hooks.onBeforeCreated Overrides.js:136
Ext.apply.doProcess ext-all-debug.js:4003
Ext.apply.doProcess ext-all-debug.js:4008
Manager.registerPostprocessor.uses ext-all-debug.js:6037
Ext.apply.require ext-all-debug.js:5771
Manager.registerPostprocessor.uses ext-all-debug.js:6004
Ext.apply.doProcess ext-all-debug.js:4007
Ext.apply.doProcess ext-all-debug.js:4008
Ext.apply.process ext-all-debug.js:3995
Ext.Class.ExtClass ext-all-debug.js:3911
Ext.ClassManager.create ext-all-debug.js:4676
Ext.apply.define ext-all-debug.js:5095
(anonymous function) NicePerson.js:1

The Overrides.js defines your override with the FixedModel.

I implemented something like this:

Ext.define('Person', {
extend: 'FixedModel',
fields: [
'id',
'firstName',
'lastName'
]
});

Ext.define('NicePerson', {
extend: 'Person',
fields: [
{name: 'fullName',
convert: function(value, record) {
return record.get('firstName') + " " + record.get('lastName');
}
}
]
});

Do you maybe know how to fix this bug/problem in ExtJS 4.1.0?

Thank you again!

oilid
27 Apr 2012, 5:10 AM
Ah, I already found this bug report and the corresponding override:
http://www.sencha.com/forum/showthread.php?198678-4.1.0.-Ext.data.Field-gt-Uncaught-TypeError-Object-lt-Object-gt-has-no-method-convert&p=789099&viewfull=1

This helps ;-)

vietits
27 Apr 2012, 5:56 AM
I think this is also a bug. This bug happens when you extend from a model that has one or more auto field. Below is my fix for this bug. Hope that will help.


Ext.define('FixedDataTypes', {
override: 'Ext.data.Types',
AUTO: {
convert: function(v){
return v;
},
sortType: Ext.data.SortTypes.none,
type: 'auto'
}
});

BTW, I have posted this bug on the Bugs branch. See it here http://www.sencha.com/forum/showthread.php?198962-Problem-in-extending-from-a-model-with-auto-fields