ExtJS model associations with jsonapi specification - json

We are creating a new version our API (v2) adopting the JSON:API specification (https://jsonapi.org/). I'm not being able to port the ExtJS model associations (belongs_to) to the new pattern.
The ExtJS documentation only shows how to use a nested relation in the same root node (https://docs.sencha.com/extjs/4.2.2/#!/api/Ext.data.association.Association).
v1 data (sample):
{
"data": [
{
"id": 1,
"description": "Software Development",
"area_id": 1,
"area": {
"id": 1,
"code": "01",
"description": "Headquarters"
}
},
],
"meta": {
"success": true,
"count": 1
}
}
v2 data (sample):
{
"data": [
{
"id": "1",
"type": "maint_service_nature",
"attributes": {
"id": 1,
"description": "Software Development",
"area_id": 1
},
"relationships": {
"area": {
"data": {
"id": "1",
"type": "area"
}
}
}
}
],
"included": [
{
"id": "1",
"type": "area",
"attributes": {
"id": 1,
"code": "01",
"description": "Headquarters"
}
}
],
"meta": {
"success": true,
"count": 1
}
}
My model:
Ext.define('Suite.model.MaintServiceNature', {
extend: 'Ext.data.Model',
fields: [
{ desc: "Id", name: 'id', type: 'int', useNull: true },
{ desc: "Area", name: 'area_id', type: 'int', useNull: true },
{ desc: "Description", name: 'description', type: 'string', useNull: true, tableIdentification: true }
],
associations: [
{
type: 'belongsTo',
model: 'Suite.model.Area',
foreignKey: 'area_id',
associationKey: 'area',
instanceName: 'Area',
getterName: 'getArea',
setterName: 'setArea',
reader: {
type: 'json',
root: false
}
}
],
proxy: {
type: 'rest',
url: App.getConf('restBaseUrlV2') + '/maint_service_natures',
reader: {
type: 'json',
root: 'data',
record: 'attributes',
totalProperty: 'meta.count',
successProperty: 'meta.success',
messageProperty: 'meta.errors'
}
}
});
Any ideias on how to setup the association to work with the v2 data?

I'm honestly taking a stab at this one... I haven't used Ext JS 4 in years, and I wouldn't structure my JSON like JSON:API does, but I think the only way you can accomplish this is by rolling your own reader class. Given that you have generic properties for your data structure, this reader should work for all scenarios... although, I'm not too familiar with JSON:API, so I could be totally wrong. Either way, this is what I've come up with.
Ext.application({
name: 'Fiddle',
launch: function () {
Ext.define('MyReader', {
extend: 'Ext.data.reader.Json',
alias: 'reader.myReader',
root: 'data',
totalProperty: 'meta.count',
successProperty: 'meta.success',
messageProperty: 'meta.errors',
/**
* #override
*/
extractData: function (root) {
var me = this,
ModelClass = me.model,
length = root.length,
records = new Array(length),
dataConverter,
convertedValues, node, record, i;
for (i = 0; i < length; i++) {
node = root[i];
var attrs = node.attributes;
if (node.isModel) {
// If we're given a model instance in the data, just push it on
// without doing any conversion
records[i] = node;
} else {
// Create a record with an empty data object.
// Populate that data object by extracting and converting field values from raw data.
// Must pass the ID to use because we pass no data for the constructor to pluck an ID from
records[i] = record = new ModelClass(undefined, me.getId(attrs), attrs, convertedValues = {});
// If the server did not include an id in the response data, the Model constructor will mark the record as phantom.
// We need to set phantom to false here because records created from a server response using a reader by definition are not phantom records.
record.phantom = false;
// Use generated function to extract all fields at once
me.convertRecordData(convertedValues, attrs, record, me.applyDefaults);
if (me.implicitIncludes && record.associations.length) {
me.readAssociated(record, node);
}
}
}
return records;
}
});
Ext.define('Suite.model.Area', {
extend: 'Ext.data.Model',
fields: [{
name: 'type',
type: 'string'
}]
});
Ext.define('Suite.model.MaintServiceNature', {
extend: 'Ext.data.Model',
fields: [{
desc: "Id",
name: 'id',
type: 'int',
useNull: true
}, {
desc: "Area",
name: 'area_id',
type: 'int',
useNull: true
}, {
desc: "Description",
name: 'description',
type: 'string',
useNull: true,
tableIdentification: true
}],
associations: [{
type: 'belongsTo',
model: 'Suite.model.Area',
associatedName: 'Area',
foreignKey: 'area_id',
associationKey: 'relationships.area.data',
instanceName: 'Area',
getterName: 'getArea',
setterName: 'setArea'
}],
proxy: {
type: 'rest',
url: 'data1.json',
reader: {
type: 'myReader'
}
}
});
Suite.model.MaintServiceNature.load(null, {
callback: function (record) {
console.log(record.getData(true));
}
});
}
});

Related

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.

ExtJS loading nested JSON data in grid

I have a php-script, which returns the following JSON
[
{
"Code":0,
"Message":"No problem"
},
{
"name":"o016561",
"status":1,
"locks":[
{
"ztn":"155320",
"dtn":"20131111",
"idn":"78"
},
{
"ztn":"155320",
"dtn":"20131111",
"idn":"91"
}
]
},
{
"name":"o011111",
"status":1,
"locks":[
{
"ztn":"155320",
"dtn":"20131111",
"idn":"91"
}
]
},
{
"name":"o019999",
"status":0,
"locks":[
]
},
{
"name":"o020000",
"status":0,
"locks":[
]
},
{
"name":"o020001",
"status":0,
"locks":[
]
}
]
Edit:
The grid should look something like this:
I've been able to load name and status into my grid - so far so good. But the more important part is, that I need the nested data in the locks-array being loaded into my grid, but I just can't get my code working. Any help would be appreciated.
I'm using ExtJS 4.2 if that matters.
Edit 2:
I tried
Ext.define("Locks", {
extend: 'Ext.data.Model',
fields: [
'ztn',
'dtn',
'idn'
]
});
Ext.define("ConnectionModel", {
extend: 'Ext.data.Model',
fields: ['name', 'status'],
hasMany: [{
model: 'Locks',
name: 'locks'
}]
});
var store = Ext.create('Ext.data.Store', {
model: "ConnectionModel",
autoLoad: true,
proxy: {
type: 'ajax',
reader: {
type: 'json',
root: 'name'
}
}
});
but it seemed to be wrong in multiple ways...
and it would be awesome if ztn and dtn could be displayed just seperated with a whitespace in the same column
You can add a renderer to the column. In that renderer you can do anything with the record...
Here's a working fiddle:
http://jsfiddle.net/Vandeplas/MWeGa/3/
Ext.create('Ext.grid.Panel', {
title: 'test',
store: store,
columns: [{
text: 'Name',
dataIndex: 'name'
}, {
text: 'Status',
dataIndex: 'status'
}, {
text: 'idn',
renderer: function (value, metaData, record, rowIdx, colIdx, store, view) {
values = [];
record.locks().each(function(lock){
values.push(lock.get('idn'));
});
return values.join('<br\>');
}
}, {
text: 'ztn + dtn',
renderer: function (value, metaData, record, rowIdx, colIdx, store, view) {
values = [];
record.locks().each(function(lock){
values.push(lock.get('ztn') + ' ' + lock.get('dtn'));
});
return values.join('<br\>');
}
}],
height: 200,
width: 600,
renderTo: Ext.getBody()
});
Note
If you have control over your backend you better change the form of your data more like this:
{
"code": 0,
"message": "No problem",
"success": true,
"data": [
{
"name": "o016561",
"status": 1,
"locks": [
{
"ztn": "155320",
"dtn": "20131111",
"idn": "78"
},
{
"ztn": "155320",
"dtn": "20131111",
"idn": "91"
}
]
},
{
"name": "o011111",
"status": 1,
"locks": [
{
"ztn": "155320",
"dtn": "20131111",
"idn": "91"
}
]
}
]
}
That way you don't mix your control data (success, message, code,...) with your data and the proxy picks it up correctly (it can be a cause of the problems your experiencing). I added a success boolean => Ext picks it up and goes to the failure handler. It helps a lot with your exception handling.
Here is the proxy for it:
proxy: {
type: 'ajax',
api: {
read: ___URL____
},
reader: {
type: 'json',
root: 'data',
messageProperty: 'message'
}
}

Sencha Touch 2.2 load store from JSON, data goes to raw column

I'm trying to load a store from JSON received from webservices. But all the data from the JSON goes under the 'raw' column of the items in the store...
I can't figure out why, my code seems correct.
Any help is welcome.
My Model :
Ext.define('App.model.Node', {
extend: 'Ext.data.Model',
config: {
fields: [
{ name: 'id', type: 'int' },
{ name: 'version', type: 'int' },
{ name: 'user_id', type: 'int' },
{ name: 'tstamp', type: 'date' },
{ name: 'changeset_id', type: 'int' },
{ name: 'tags', type: 'string' },
{ name: 'geom', type: 'string'}
],
idProperty: 'id'
}
});
My Store :
Ext.define('App.store.NodeStore', {
extend: 'Ext.data.Store',
xtype: 'nodestore',
requires: [
'Ext.data.proxy.Rest'
],
config: {
model: 'App.model.Node',
storeId: 'nodeStore',
autoLoad: true,
proxy: {
type:'rest',
url:'http://localhost/server/nodes',
reader: {
type:'json',
rootProperty: 'nodes'
},
noCache: false,
limitParam: false,
headers: {
'Accept' : 'application/json'
}
}
}
});
My JSON :
{
"nodes": [
{
"id": "454467",
"version": 6,
"user_id": 52015,
"tstamp": "2008-12-27 21:38:45",
"changeset_id": "634766",
"tags": "",
"geom": "0101000020E6100000409CD1A0B29321405455682096804740"
},
{
"id": "454468",
"version": 8,
"user_id": 52015,
"tstamp": "2009-12-23 20:47:15",
"changeset_id": "3437205",
"tags": "",
"geom": "0101000020E6100000357C0BEBC69321409EC02ACD9C804740"
},
{
"id": "454469",
"version": 7,
"user_id": 52015,
"tstamp": "2009-12-23 20:47:15",
"changeset_id": "3437205",
"tags": "",
"geom": "0101000020E6100000347914F8D4932140B8BBBD5AA4804740"
}
]
}
And when I do a
var nodeStore = Ext.getStore('nodeStore');
nodeStore.load();
console.log(nodeStore.getData());
we can see the following object, with my data in the raw column under items...
I figured it out, my code is correct and the only thing missing is a callback in the load() function :
nodeStore.load({
callback: function(records, operation, success) {
console.log(records);
console.log(nodeStore.getCount());
nodeStore.each(function(element) {
console.log(element.data.id);
});
},
scope: this,
});
The problem was I was trying to access the store before it loaded the data. Now I'm waiting that all the data is loaded to access it, and it works.

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