PDA

View Full Version : Models: how to map a field to a combination of properties



momesana
12 Aug 2011, 2:31 AM
Is there a way to map a field in a model to a combination of two properties? Basically the question is how to inject a mediator between what is loaded with a proxy and what is given to a model or rather the way to generate a new data object from the response fetched via ajax (or whatever mechanism is used).

Let's take the following json data returned by the server example:

{
"success": true,
"results": [{
"id": 11,
"firstName" : "John",
"lastName": "Doe"
}, {
"id": 12,
"firstName" : "Max",
"lastName": "Mustermann"
}
]
}

Now I want to use a store for a combobox and then have each entry being displayed as the combination of first and lastname. I would need to create a model with four fields: The one above plus one additional field that I can use as the displayProperty in the combobox. How can I do that without needing to change ther serverside to return an additional property thereby increasing traffic? If that's not possible in an easy way, how can it best be achieved? Do I have to override the reader of the proxy that does the loading?

Any help would be appreciated.

Thanx in advance

slemmon
12 Aug 2011, 5:06 AM
You're in luck! I just ran across this yesterday. Had the same question myself.
http://docs.sencha.com/ext-js/4-0/#/api/Ext.data.Field-cfg-convert
(http://docs.sencha.com/ext-js/4-0/#/api/Ext.data.Field-cfg-convert)
The convert method will take multiple data points and allow you to combine them / manipulate them within the store as a unique field. Pretty cool I thought. Wish I had known about this back on an Ext 3 project. I didn't realize it existed so I would listen for a load event, run through the store and grab my 'ingredients', and ADD the value to store and then mark it as clean - or something like that. Turns out Ext 3 had convert, too. :">

jay@moduscreate.com
12 Aug 2011, 8:33 AM
Convert is the way to go.



{
name : 'myField',
mapping : 'myField',
convert : function(v, record) {
return v + ' ' + record.data.someOtherField;
}
}

momesana
12 Aug 2011, 9:25 AM
Thank you both for the answer.

@jgarcia-tdg-i.com

Can't wait to get my hands on Ext JS in Action 4.0 and of course Sencha Touch in Action :-). I basically learned most of Sencha watching your online video tutorials and reading "Ext JS in Action 3".

jay@moduscreate.com
12 Aug 2011, 9:40 AM
Awesome dude:)

Please mark this as answered.

momesana
12 Aug 2011, 10:51 AM
Seems like I cannot use member functions in the call to convert. Is there a reason for that? It doesn't spit out any error message but also doesn't call the function. I could take this.xy as the function name and it wouldn't complain that the function is not defined.

I tried this:

/**
* @classDescription This Model is used to represent a site.
* It has four properties: id, name code and displayName. each
* site is associated with a number of buildings.
*/
Ext.define('zf.model.Site', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int', mapping: 'id'},
{name: 'name', type: 'string', mapping: 'name'},
{name: 'code', type: 'string', mapping: 'abbr'},
{name: 'displayName', convert: this.getDisplayName}
],
hasMany : {model: 'Building', name: 'buildings'},
getDisplayName: function (v, record) { return record.get('code') + '-' + record.get('name'); }
});

Using an anonymous function like this however poses no problems:


/**
* @classDescription This Model is used to represent a site.
* It has four properties: id, name code and displayName. each
* site has a number of buildings.
*/
Ext.define('zf.model.Site', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int', mapping: 'id'},
{name: 'name', type: 'string', mapping: 'name'},
{name: 'code', type: 'string', mapping: 'abbr'},
{name: 'displayName', convert: function (v, record) {
return record.get('code') + '-' + record.get('name');
}}
],
hasMany : {model: 'Building', name: 'buildings'}


});


Any ideas?

Thanks in advance

skirtle
12 Aug 2011, 12:59 PM
The variable this won't be what you think it is. The code that isn't working is equivalent to the following:


var myFn = this.getDisplayName;

Ext.define('zf.model.Site', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int', mapping: 'id'},
{name: 'name', type: 'string', mapping: 'name'},
{name: 'code', type: 'string', mapping: 'abbr'},
{name: 'displayName', convert: myFn}
],
hasMany : {model: 'Building', name: 'buildings'},
getDisplayName: function (v, record) { return record.get('code') + '-' + record.get('name'); }
});

Hopefully it's obvious in this case that this will be the global window object and myFn will be undefined. Specifying a convert function of undefined is just the same as not specifying one at all. Given this, it should come as no surprise that there is no error message even if you use this.xy.

momesana
12 Aug 2011, 3:04 PM
Well, I assumed that this was a context problem, yet I can't comprehend why this is the case. After all the stuff gets initialized inside the class definition and it's not within some other function that could have changed the scope.

mberrie
13 Aug 2011, 8:42 AM
Remember that Ext.define is just an ordinary method call.
(This is different from many other often compiled languages that have an explicit class construct and syntax).

Ext.define accepts two method parameters, a string for the name and a config object for defining properties and methods.

So the code you posted is just a simple javascript statement that calls a method (Ext.define) and passes two method parameters (name and config object).

Before the javascript engine can call the method, it has to create the objects that you want to pass in as method parameters. In order to create those objects, it will evaluate the code that defines each parameter.

Hence, 'this.displayName' (as part of the object that constitutes the second parameter) is evaluated BEFORE control is passed to Ext.define.
And the engine will not pass the code statement, 'this.displayName', but the *result* of the evaluation of this code statement.

With this explanation in mind, go back to skirtles post!

skirtle
13 Aug 2011, 11:27 AM
@mberrie: Nicely explained.

I guess the key thing to realize is that the value of this is determined only by how the current function was invoked. Nothing else matters. In this case we aren't in a function, so this falls back to being the global object, often referred to as window.

I know a lot of people find this confusing. It's very different to how it works in languages like Java where everything is about classes.

momesana
13 Aug 2011, 8:15 PM
Thanks for the clarification. I usually tend to set a lot of event-handlers inside the initComponent function where this does refer to the object that "Ext.define" defines. But that of course is a function that is evaluated when the object described by Ext.define has already been created, hence running in that context. I just overlooked the fact that we were not inside a function defined as a property of an object created by Ext.define but inside Ext.define itself. Again thanks for shedding light upon this peculiar aspect of javascript.