Root property of a nested JSON or model in sencha - json

i have a json in the following format:
{
"collection": [
{
"id": 4,
"tickets": {
"collection": [
{
"inner_id": 8,
},
{
"inner_id": 10,
}
],
"count": 2,
"type": "collection"
},
},
{
"id": 5,
"tickets": {
"collection": [
{
"inner_id": 1,
},
{
"inner_id": 2,
}
],
"count": 2,
"type": "collection"
},
},
]
}
For this particular JSON i created the models as:
Ext.define("myProject.model.ABC", {
extend: "Ext.data.Model",
config: {
idProperty: "id",
fields:[
{name: "id", type: "int" },
],
hasMany: [
{
model: "myProject.model.XYZ",
name: "tickets",
associationKey: "tickets",
},
],
}
});
And second store as:
Ext.define("myProject.model.XYZ", {
extend: "Ext.data.Model",
config: {
// idProperty: "id",
fields:[
{name: "inner_id", type: "int" },
],
belongsTo: 'myProject.model.ABC'
}
});
But now i am confused. How do i populate the second store with a root property of collection again.
I know one way is to easily change the json so that there is no collection child inside tickets but i dont want to do that.
Any help would be appreciated. I have simplified the JSON for an easy example.
EDIT:
To be more clear, is there a way i can directly create a model which will read the Collection array inside the tickets object.
EDIT:
Adding the store which populates the model ABC for more understanding
Ext.define("myProject.store.ABCs", {
extend: "Ext.data.Store",
config: {
model: "myProject.model.ABC",
autoLoad: false,
proxy: {
type: "ajax",
url: '', //myURL
reader: {
type: "json",
rootProperty: "collection", // this is the first collection
},
},
}
});
This store loads the ABC model correctly but now i want to load the XYZ model which can load the inner array of collection

belongsTo should be define as follows :
Ext.define('Product', {
extend: 'Ext.data.Model',
fields: [
{ name: 'id', type: 'int' },
{ name: 'category_id', type: 'int' },
{ name: 'name', type: 'string' }
],
associations: [
{ type: 'belongsTo', model: 'Category' }
]
Have you read the doc? They specified the root property.
The name of the property which contains the data items corresponding to the Model(s) for which this Reader is configured. For JSON reader it's a property name (or a dot-separated list of property names if the root is nested). For XML reader it's a CSS selector. For Array reader the root is not applicable since the data is assumed to be a single-level array of arrays.
By default the natural root of the data will be used: the root JSON array, the root XML element, or the array.
The data packet value for this property should be an empty array to clear the data or show no data.
Sometimes the JSON structure is even more complicated. Document databases like CouchDB often provide metadata around each record inside a nested structure like this:
{
"total": 122,
"offset": 0,
"users": [
{
"id": "ed-spencer-1",
"value": 1,
"user": {
"id": 1,
"name": "Ed Spencer",
"email": "ed#sencha.com"
}
}
]
}
In the case above the record data is nested an additional level inside the "users" array as each "user" item has additional metadata surrounding it ('id' and 'value' in this case). To parse data out of each "user" item in the JSON above we need to specify the record configuration like this:
reader: {
type : 'json',
root : 'users',
record: 'user'
}
root as a Function
reader: {
type : 'json',
root : function (obj) {
// I can't reproduce your problem
// so you should check in your console collection.id is right
return obj.collection.id
}
}
// Or, we can use dot notation
reader: {
type : 'json',
root : collection[0].tickets.collection
}

There are two ways to solve this problem. After researching extensively, i found two solutions...
SOLUTION 1:
Instead of making a second model, just create one model and create an array field with type as "auto"
Ext.define("myProject.model.ABC", {
extend: "Ext.data.Model",
config: {
idProperty: "id",
fields:[
{name: "id", type: "int" },
{ name: "tickets", convert: function(value, record) {
if(value.collection instanceof Array) {
return value.collection;
} else {
return [value.collection]; // Convert to an Array
}
return value.collection;
}
}
],
}
});
Now you can refer to an array of tickets from records by:
record.get('tickets')
SOLUTION 2:
Create three model instead of two.
Model 1:
hasOne Association with Tickets
Model 2:
hasMany association with Collection
Model 3:
has all the fields of the innermost array
I can give an example if its not clear enough

Related

Parse nested json with a proxy in a Sencha Touch 2 store using rootProperty

I have a JSON response that is nested like the following (simplified, but same format):
{
"response":{
"v":"1.0",
"users":[
{
"firstName":"Nicole",
"LastName":"A",
},
{
"firstName":"John",
"LastName":"B",
},
{
"firstName":"Bob",
"LastName":"C",
}
],
}
}
Here is the model:
Ext.define('MyApp.model.User', {
extend: 'Ext.data.Model',
requires: [
'Ext.data.Field'
],
config: {
fields: [
{
name: 'firstName'
},
{
name: 'lastName'
}
]
}
});
I am starting from the sencha architect tutorial for CityBars, so most of the code should be quite basic, and I am just trying to get the users from the json response loaded. Here is the controller:
Ext.define('MyApp.controller.User', {
extend: 'Ext.app.Controller',
launch: function() {
var me = this;
Ext.Viewport.setMasked({ message: 'Loading Attendees...' });
me.getUsers(function (store) {
me.getDataList().setStore(store);
});
},
getUsers: function(callback) {
var store = Ext.data.StoreManager.lookup('UserStore'),
url = 'http://urltogetjsonresponse'
store.getProxy().setUrl(url);
store.load(function() {
callback(store);
});
},
});
Here is the store:
Ext.define('MyApp.store.UserStore', {
extend: 'Ext.data.Store',
requires: [
'MyApp.model.User',
'Ext.data.proxy.JsonP',
'Ext.data.reader.Json'
],
config: {
model: 'MyApp.model.User',
storeId: 'UserStore',
proxy: {
type: 'jsonp',
reader: {
type: 'json',
rootProperty: 'response.user'
}
}
}
});
I tried 'response.user' but it did not work for me. I have already looked all over and know that using rootProperty: 'user' would work, if the users attribute were at the same level as "response" instead of nested under it. I have also tried adding record: 'users' but that did not seem to work either.
If anybody knows if this is doable and has an easy solution to this, that would be great. I don't actually understand how the proxy works, so if anybody can explain a bit about that, it would be helpful too. Thanks.
Taken from Sencha's documentation about the JSON reader :
{
"count": 1,
"ok": true,
"msg": "Users found",
"users": [{
"userId": 123,
"name": "Ed Spencer",
"email": "ed#sencha.com"
}],
"metaData": {
"idProperty": 'userId',
"rootProperty": "users",
"totalProperty": 'count',
"successProperty": 'ok',
"messageProperty": 'msg'
}
}
The rootProperty here is 'users', so you'll need to specify users (which is the name of the array containing your instances of model) and not user .

Sort a store data based on inner JSON field sencha

i have a json in the following format:
{
"collection": [
{
"id": 4,
"tickets": [
{
"price": 40,
},
{
"price": 50,
}
],
},
{
"id": 1,
"tickets": [
{
"price": 10,
},
{
"price": 15,
}
]
},
]
}
STORE:
Ext.define("myProject.store.ABCs", {
extend: "Ext.data.Store",
config: {
model: "myProject.model.ABC",
autoLoad: false,
proxy: {
type: "ajax",
url: '', //myURL
reader: {
type: "json",
rootProperty: "collection", // this is the first collection
},
},
}
});
For this particular JSON i created the models as:
Ext.define("myProject.model.ABC", {
extend: "Ext.data.Model",
config: {
idProperty: "id",
fields:[
{name: "id", type: "int" },
],
hasMany: [
{
model: "myProject.model.XYZ",
name: "tickets",
associationKey: "tickets",
},
],
}
});
And second model as:
Ext.define("myProject.model.XYZ", {
extend: "Ext.data.Model",
config: {
// idProperty: "id",
fields:[
{name: "inner_id", type: "int" },
],
belongsTo: 'myProject.model.ABC'
}
});
This particular code creates a store and populates the models correctly.
var store = Ext.getStore('ABCs');
Now i want to sort this store based on store.tickets().getAt(0).get('price') that is sort the ABC records based on XYZ's first price property.
In the above json. ABC Records will be: [{id:4}, {id:1}]
But since first price in XYZ (40 > 10), i want to sort them and create [{id:1}, {id:4}]
Take a look at the Ext.util.Sorter class, where you can set a sorterFn. See the example at the top of the page - you should be able to simply write the logic for sorting records the way you describe.

Sencha touch 2 load store and associated store with nested JSON

I've started creating a Sencha Touch 2 app that has two models defined, Change and Configuration. A Change belongsTo a Configuration. Both of the models also have a store setup. On the Changes store I have a proxy setup to request data that comes back in JSON format. The JSON data has a Change with a nested configuration. The Change loads just fine but when I try and get the associated Configuration from the Change instance it isn't working.
I've defined the models like this:
Change model:
Ext.define('Changes.model.Change', {
extend: 'Ext.data.Model',
xtype: 'changemodel',
config: {
fields: [
{name:'id', type:'int'},
{name:'changeId', type:'string'},
{name:'briefDescription', type:'string'},
{name:'configuration_id', type:'int'}
],
associations: [{
type:'belongsTo',
model:'Changes.model.Configuration',
primaryKey: 'id',
foreignKey: 'configuration_id',
associationKey: 'configurations'
}],
},
});
Configuration Model:
Ext.define('Changes.model.Configuration', {
extend: 'Ext.data.Model',
xtype: 'configurationmodel',
config: {
fields: [
{ name: 'id', type: 'int'},
{ name: 'longName', type: 'string' },
{ name: 'ciName', type: 'string' },
],
hasMany: {model: 'Change', name: 'changes'}
}
});
Each model has a store.
Changes store:
Ext.define('Changes.store.Changes', {
extend: 'Ext.data.Store',
requires: 'Changes.model.Change',
config: {
model: 'Changes.model.Change',
proxy: {
type: 'ajax',
url: 'services/changes.php',
reader: {
type: 'json',
rootProperty: 'changes'
}
},
sorters: [{ property: 'briefDescription', direction: 'ASC'}],
}
});
Configurations store:
Ext.define('Changes.store.Configurations', {
extend: 'Ext.data.Store',
requires: ['Ext.data.proxy.LocalStorage'],
config: {
model: 'Changes.model.Configuration',
grouper: {
sortProperty: "ciName",
direction: "DESC",
groupFn: function (record) {
return record.get('ciName')[0];
}
},
proxy: {
type: 'ajax',
url: 'services/configurationItems.php',
reader: {
type: 'json',
rootProperty: 'configurationItems'
}
}
}
});
My JSON that is being returned from services/changes.php looks like this:
{
"success": true,
"changes": [
{
"id": 1,
"changeId": "XYZ19178263",
"briefDescription": "Loaded from Proxy",
"configuration_id": 3,
"configurations": [
{
"id": "3",
"longName": "999-99_-_windows_7_desktop.Computer.Windows",
"ciName": "999-99_-_windows_7_desktop"
}
]
}
]
}
In the browser's console I can issue the following commands:
Changes.myStore = Ext.getStore('Changes');
Changes.myStore.load();
Changes.record = Changes.myStore.findRecord('id', '1');
Changes.record.getAssociatedData()
The last command will return an object with a Configuration object inside but all of the field values show null except for id which appears to be set to a random value:
Object
Configuration: Object
ciName: null
id: "ext-record-11"
longName: null
Can anyone see why the nested Configuration instance in my JSON isn't being saved? And should the nested Configuration instance in the JSON be added to the Configurations store automatically?
Sadly this just doesn't work. Appears to be a shortcoming of the framework. You can't load and save associated (aggregated/nested) objects. You need to flatten out your structure and then associate objects in code yourself.
So for example... your JSON would then be...
{
"success": true,
"changes": [
{
"id": 1,
"changeId": "XYZ19178263",
"briefDescription": "Loaded from Proxy"
}
]
}
And a second JSON file/response as follows:
{
"success": true,
"configurations": [
{
"id": "3",
"longName": "999-99_-_windows_7_desktop.Computer.Windows",
"ciName": "999-99_-_windows_7_desktop",
"change_id": 1
}
]
}
Tinashe's answer is not correct. You can certainly get nested models.
You have your associations backward with respect to your JSON, however.
"changes": [
{
"id": 1,
"changeId": "XYZ19178263",
"briefDescription": "Loaded from Proxy",
"configuration_id": 3,
"configurations": [
{
"id": "3",
"longName": "999-99_-_windows_7_desktop.Computer.Windows",
"ciName": "999-99_-_windows_7_desktop"
}
]
}
]
means a change hasMany configurations, but you are saying it belongsTo it. This is the correct JSON:
"changes": [
{
"id": 1,
"changeId": "XYZ19178263",
"briefDescription": "Loaded from Proxy",
"configuration_id": 3,
"configuration":
{
"id": "3",
"longName": "999-99_-_windows_7_desktop.Computer.Windows",
"ciName": "999-99_-_windows_7_desktop"
}
}
]
Either way, you need to set getterName (setterName if you like too) on the relationship:
associations: [{
type:'belongsTo',
model:'Changes.model.Configuration',
primaryKey: 'id',
foreignKey: 'configuration_id',
associationKey: 'configuration',
getterName:'getConfiguration'
}]
Then after the store loads, you can call myChangeModel.getConfiguration();

Sencha Touch: How to get data from complex JSON objects

I am not able retrieve records from complex json objects but i am able to get the data when using an TPL.
Please see the below example codes:
JSON:
{
"lists": [
{
"title": "Groceries",
"id": "1",
"items": [
{
"text": "contact solution - COUPON",
"listId": "1",
"id": "4",
"leaf": "true"
},
{
"text": "Falafel (bulk)",
"listId": "1",
"id": "161",
"leaf": "true"
},
{
"text": "brita filters",
"listId": "1",
"id": "166",
"leaf": "false"
}
]
}
]
Model:
Ext.regModel("TopModel",
{
fields: [
{ name: 'title', type: 'string' },
{ name: 'id', type: 'string' },
],
hasMany: [
{ model: 'SubModel', name: 'items' }
],
proxy: {
type: 'ajax',
url : 'test/lists.json',
actionMethods: {
create: 'POST',
destroy: 'POST',
read: 'POST',
update: 'POST'
},
reader: {
type: 'json',
root: function(data) {
return data.lists || [];
}
}
}
});
Ext.regModel("SubModel",
{
fields: [
{ name: 'text', type: 'string'},
{ name: 'listId', type: 'string'},
{ name: 'id', type: 'string'},
{ name: 'leaf', type: 'string'}
]
});
In my View file, i have defined the store as below.
this.stores = new Ext.data.Store({
clearOnPageLoad: false,
autoLoad:true,
model:'TopModel',
getGroupString: function(record) {
return record.get('leaf');
}
});
});
I am not able to retrieve the values for record.get('leaf') as this refers to the child model items. When I tried to print it, it prints as undefined.
How to access the child attributes? Here 'items' is listed as an array.
I tried to display the data using list as below. All the records are displayed but the problem is that it is picked as one whole item instead of separate list for each item.
var list = new Ext.List({
cls: 'big-list',
grouped : true,
emptyText: '<div>No data found</div>',
itemTpl: ['<div><tpl for="items">',
'<div>',
'{id} {text}',
'</div>',
'</tpl></div>',
],
store: this.stores,
listeners: {
itemtap: this.onListItemTap,
scope: this
}
});
Kindly help me on how to get the list items to be displayed as individual records.
you can use online json parser(http://json.parser.online.fr/) to parse json xml from parser data you easily seprate objects and arrays and you get all data which are require for you..i hope this help you

How to Display Nested Json data in EXTJS 4 Grids?

I am working on ExtJS 4.0 and I want to display nested JSON data in a grid. For this I use the example given in Ext.data.reader.Reader docs, "Loading Nested Data". It is good and simple but now I want to display this data in a grid. How do I set dataIndex?
This is my sample model and store:
Ext.define("data", {
extend: 'Ext.data.Model',
fields: ['year', 'state'],
hasMany: {
model: 'record',
name: 'record'
},
proxy: {
type: 'rest',
url: 'Column.json.php',
reader: {
type: 'json',
root: 'data'
}
}
});
Ext.define("record", {
extend: 'Ext.data.Model',
fields: ['id', 'autorization', 'expendture'],
belongsTo: 'User'
});
var store1 = new Ext.data.Store({
model: "data"
});
And my JSON:
{
"data": [{
"year": "2010",
"state": "MP",
"record": [{
"id": "auth",
"autorization": "9.201"
}, {
"id": "exp",
"expendture": "1.250"
}]
}]
}
I want to read autorization and expendture with id
You have to do it at the Model/Record level using the mapping confg in fields, so you'd do something like this:
Ext.define("record", {
extend: 'Ext.data.Model',
fields: [
'id',
{name: 'autorization', mapping: 'record[0].autorization'},
{name: 'expendture', mapping: 'record[1].expendture'}
],
belongsTo: 'User'
});
It's good to note that it is probably quicker to ask questions over on the Sencha Forums.
I want to point out that Store.loadData does not respect the field mapping
The issue is that the Sencha team changed loadData's behavior, AND it's not something that's documented in a way that is clear. So if you are using it, add the following to your code base (above your app code, but below ext-all.js):
Ext.override(Ext.data.Store, {
loadDataViaReader : function(data, append) {
var me = this,
result = me.proxy.reader.read(data),
records = result.records;
me.loadRecords(records, { addRecords: append });
me.fireEvent('load', me, result.records, true);
}
});
then use:
mystore.loadDataViaReader(data)