PDA

View Full Version : Import of JSON nested data within ExtJS4 data model



simreno
22 Dec 2011, 1:29 AM
Hi,

I am having trouble to implement the ExtJS4 Data.view.Model classes for the JSON data the Google Product Search API returns.

Below are the codes I am using: a simple data Store and Controller, moreover a data sample returned by the Google API and the Model I have defined to import them. I've put below some mock data to avoid the HTTP GET request.
I retrieve 10 items (products) from the store, as requested through the HTTPs GET request, however my models do not get automatically populated with data.

I guess my mapping is wrong, but did not find out the way to match the nested structure of the JSON answer towards my ExtJS4 data / association model.

Any help would be welcome! a minor challenge for some of you?..
I packed all files in the attached zip file: index.html, app.js, model, store, controller: ready to run!

Sample JSON-formatted data returned by the Google Product Search API:
File 'res/data/storegproducts.js' or a sub-sample can be found in the store file 'app/store.GProductsV1.js'


{
"items" : [{
"product" : {
"googleId": "5268148080094234388",
"author" : {
"name" : "Amazon.fr"
},
"title" : "Samsung - Galaxy S II - Smartphone - Quadribande/HSDPA - Bluetooth - Android - Noir",
"description" : "Fake Description text",
"link" : "http://www.amazon.fr/dp/B0052EWH32/ref=asc_df_B0052EWH325752505/?tag=googlefrshopp-21&creative=22926&creativeASIN=B0052EWH32&linkCode=asn",
"brand" : "Samsung",
"condition" : "new",
"inventories" : [{
"price" : 470.53,
"currency" : "EUR"
}],
"images" : [{
"link" : "http://ecx.images-amazon.com/images/I/51xmh%2BwPYHL.jpg"
}]
}
}]
}


The ExtJS4 data model I am trying to fit with previous data. I guess there is a mess with the 'item(s)' and the 'product' element association..
File 'app/model/Item.js'


Ext.define('App.model.Item', {
extend: 'Ext.data.Model',


hasMany: {model: 'Product', name: 'product', associationKey: 'product'}
// fields: [
// {name: 'product', model: 'Product'}
// ]
});


Ext.define('App.model.Product', {
extend: 'Ext.data.Model',


idProperty: 'googleId',
fields: [
{ name: 'googleId', type: 'string' },
{ name: 'title', type: 'string' },
{ name: 'description', type: 'string' },
{ name: 'link', type: 'string' },
{ name: 'brand', type: 'string' },
{ name: 'condition', type: 'string' }
],
validations: [
{ type: 'presence', field: 'googleId' },
{ type: 'presence', field: 'title' }
],
hasMany: [{ model: 'Inventory', name: 'inventories'}
, { model: 'Image', name: 'images', associationKey: 'images'}],
belongsTo: [ {model:'Item', name:'item'},
{ model: 'Author', name: 'author'}]
});


Ext.define('App.model.Author', {
extend: 'Ext.data.Model',
fields: [
{ name: 'name', type: 'string' }
],
hasMany: 'Product'
});


Ext.define('App.model.Inventory', {
extend: 'Ext.data.Model',
fields: [
{ name: 'price', type: 'float' },
{ name: 'currency', type: 'string' }
]
, belongsTo: 'Product'
});


Ext.define('App.model.Image', {
extend: 'Ext.data.Model',
fields: [
{ name: 'link', type: 'string' }
]
, belongsTo: 'Product'
});


The Store I use to retrieve data from Google API (through a JSONP proxy), or an offline JSON data file (to avoid http request since this seems not to be the problem).
File: 'app/store/GProductsV1.js'

And finally the Controller I use to test the load of data.
File: app/controller/GProductsV1.js
You will notice it tests the data model but only 'undefined' are retrieved.

Thank you for any insights you might provide!

simreno
22 Dec 2011, 2:33 AM
For those interested in this matter of modeling / wrapping a nested JSON structure as shown before, here is a simplified version of my data model:



Ext.define('App.model.Item', {
extend: 'Ext.data.Model',

idProperty: 'googleId',
fields: [
{ name: 'googleId', type: 'string', mapping: 'product.googleId' },
{ name: 'title', type: 'string', mapping: 'product.title' },
{ name: 'description', type: 'string', mapping: 'product.description' },
{ name: 'link', type: 'string', mapping: 'product.link' },
{ name: 'brand', type: 'string', mapping: 'product.brand' },
{ name: 'condition', type: 'string', mapping: 'product.condition' },

{ name: 'vendor', type: 'string', mapping: 'product.author.name' }
],
validations: [
{ type: 'presence', field: 'googleId' },
{ type: 'presence', field: 'title' }
],

// KO!
hasMany: [
{ model: 'Image', name: 'images', mapping: 'product.images'} // , associationKey: 'images'
// { model: 'Inventory', name: 'inventories', mapping: 'product.inventories'},
]
});

Ext.define('App.model.Inventory', {
extend: 'Ext.data.Model',
fields: [
{ name: 'price', type: 'float' },
{ name: 'currency', type: 'string' }
],
belongsTo: 'Item'
});

Ext.define('App.model.Image', {
extend: 'Ext.data.Model',
fields: [
{ name: 'link', type: 'string' }
],
belongsTo: 'Item'
});



I have used the 'mapping' attribute for my fields' definition, which's helped a lot, at least simplified the code and data are indeed populated there.

BUT, I still can't import/reach the subsets of data: 'images' & 'inventories'.
Any hints please?

skirtle
22 Dec 2011, 10:48 PM
I think you may find the record property useful, rather than using such long mapping names:

http://docs.sencha.com/ext-js/4-0/#!/api/Ext.data.reader.Json-cfg-record

Your associations need to use the fully-qualified model names:


model: 'App.model.Image'

simreno
23 Dec 2011, 12:55 AM
Hi Skirtle, thank your for your help!

I've integrated the record property into the proxy reader and it does indeed simplify my data model. It looks as the followings now:



Ext.define('App.model.Item', {
extend: 'Ext.data.Model',

idProperty: 'id',
fields: [
{ name: 'id', type: 'string', mapping: 'googleId' },
{ name: 'title', type: 'string' },
{ name: 'description', type: 'string' },
{ name: 'link', type: 'string' },
{ name: 'brand', type: 'string' },
{ name: 'condition', type: 'string' },

{ name: 'vendor', type: 'string', mapping: 'author.name' }
],
validations: [
{ type: 'presence', field: 'id' },
{ type: 'presence', field: 'title' }
],

hasMany: [ // 'App.model.Image','App.model.Inventory'
{ model: 'App.model.Image', name: 'images', mapping: 'images', associationKey: 'images' }, // associationKey: 'images'
{ model: 'App.model.Inventory', name: 'inventories'} // , mapping: 'inventories'
]

, proxy: {
type: 'ajax',
url: 'res/data/storegproducts.json',
reader: {
type: 'json',
root: 'items',
record: 'product'
}
}
});

Ext.define('App.model.Inventory', {
extend: 'Ext.data.Model',
fields: [
{ name: 'price', type: 'float' },
{ name: 'currency', type: 'string' }
],
belongsTo: 'App.model.Item'
});

Ext.define('App.model.Image', {
extend: 'Ext.data.Model',
fields: [
{ name: 'link', type: 'string' }
],
belongsTo: 'App.model.Item'
});


However, there must be something wrong with my associations, cause I still can't grab the data sets images and inventories. It should be easy from this point, right?

Do you notice anything wrong in my data model defintion please? I've turned arround it for hours with no clue.
1. hasMany : ['Inventory', 'Image'] - belongsTo: 'Item'
2. hasMany : ['App.model.Inventory', 'App.model.Image'] - belongsTo: 'App.model.Item'
3. hasMany : [{ model: 'App.model.Inventory', name: 'inventories'}, { model: 'App.model.Image', name: 'images'}]
4. hasMany : [{ model: 'App.model.Inventory', name: 'inventories', mapping: 'inventories'},... ]
5. hasMany : [{ model: 'App.model.Inventory', name: 'inventories', mapping: 'inventories', associationKey: 'inventories'},... ]

Aaargh!.. Please get me out of here!

cheers

Note: I test if data are populated by performing the request, once the store is loaded: item.get('images') / item.get('inventories'), these methods return 'undefined'.

skirtle
23 Dec 2011, 2:02 AM
Associations aren't accessed like that. If you just want to include an object in a record then you can but if you use associations they aren't accessible using the get method. See:

http://docs.sencha.com/ext-js/4-0/#!/api/Ext.data.HasManyAssociation

You'd do something like this:


var imagesStore = item.images();

Here the method name images comes from the name config:

http://docs.sencha.com/ext-js/4-0/#!/api/Ext.data.HasManyAssociation-cfg-name

There is no mapping config option for an association, the equivalent is associationKey though it will default to the name. I think the third variation on your list is correct:


hasMany: [
{model: 'App.model.Image', name: 'images'},
...
]

simreno
23 Dec 2011, 2:29 AM
You're damn right skirtle. I did wrongly test if my data were populated.. shame on me.
But now it is fully working! thank you a lot master!

Here is my final, and working, data model for the previously presented JSON data structure:


Ext.define('App.model.Item', {
extend: 'Ext.data.Model',

idProperty: 'id',
fields: [
{ name: 'id', type: 'string', mapping: 'googleId' },
{ name: 'title', type: 'string' },
{ name: 'description', type: 'string' },
{ name: 'link', type: 'string' },
{ name: 'brand', type: 'string' },
{ name: 'condition', type: 'string' },

{ name: 'vendor', type: 'string', mapping: 'author.name' }
],
validations: [
{ type: 'presence', field: 'id' },
{ type: 'presence', field: 'title' }
],

hasMany: [{ model: 'App.model.Image', name: 'images'},
{ model: 'App.model.Inventory', name: 'inventories'}
]

, proxy: {
type: 'ajax',
url: 'res/data/storegproducts.json',
reader: {
type: 'json',
root: 'items',
record: 'product'
}
}
});

Ext.define('App.model.Inventory', {
extend: 'Ext.data.Model',
fields: [
{ name: 'price', type: 'float' },
{ name: 'currency', type: 'string' }
],
belongsTo: 'App.model.Item'
});

Ext.define('App.model.Image', {
extend: 'Ext.data.Model',
fields: [
{ name: 'link', type: 'string' }
],
belongsTo: 'App.model.Item'
});


Time saving is a nice christmas gift, thank you for your time skirtle!
BTW the ExtJS4 development framework is a really nice one, tough first but clean and powerful then. Greatings to the whole team!