PDA

View Full Version : Need Help, Nested json data in a grid.



irfantasneem
19 Aug 2011, 6:33 PM
Hi , i am new to ExtJS and stuck at a problem. I have a deep nested json data that i have successfully loaded in a store, using multiple associated models. But for the next step, i am unable to display this data in a simple grid, please help. How can i display something in that grid which is located deep inside the json.... here is my json




{
"success" : "true",
"total":2,
"user" :
{
"id" : 12,
"email" : "abc@gmail.com",
"course" :
{
"name" : "BESE",
"institute" :
[{
"name" : "Engineering University",
"semester" :
{
"number":1,
"TCH" : 12,
"GPA" : 2.32,
"Marks":23.32,
"record" : [
{
"subject" : "Global Studies",
"CH":2,
"GP":4,
"Grade": "A+",
"Marks":99.1
},
{
"subject" : "Digital Logic Design",
"CH":4,
"GP":3.5,
"Grade": "B+",
"Marks":79.1
}
]
}
},
{
"name" : "Another University",
"semester" :
{
"number":2,
"TCH" : 22,
"GPA" : 1.32,
"Marks":13.32,
"record" : [
{
"subject" : "C++",
"CH":2,
"GP":3,
"Grade": "C+",
"Marks":59.1
},
{
"subject" : "Engg Math",
"CH":4,
"GP":2.5,
"Grade": "C",
"Marks":39.1
}
]
}
}]
}
}
}




and this is my code......





Ext.define('User', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'email', type: 'string'},
],
proxy: {
type: 'ajax',
url : 'getRecord.php',
reader: {
type: 'json',
root: 'user'
}
},
hasMany: { model: 'Course', name: 'course' ,associationKey: 'course'}

});

Ext.define('Course', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},

{name: 'name', type: 'string'},
],

belongsTo: 'User',
hasMany: { model: 'Institute', name: 'institute' ,associationKey: 'institute'}

});

Ext.define('Institute', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},

{name: 'name', type: 'string'},
],

belongsTo: 'Course',
hasMany: { model: 'Semester', name: 'semester',associationKey: 'semester' }

});

Ext.define('Semester', {
extend: 'Ext.data.Model',
fields: [
{name: 'number', type: 'int'},
{name: 'TCH', type: 'float'},
{name: 'GPA', type: 'float'},
{name: 'Marks', type: 'float'},
],

belongsTo: 'Institute',
hasMany: { model: 'Record', name: 'record',associationKey: 'record' }
});
Ext.define('Record', {
extend: 'Ext.data.Model',
fields: [
{name: 'subject', type: 'string'},
{name: 'CH', type: 'float'},
{name: 'GP', type: 'float'},
{name: 'Grade', type: 'string'},
{name: 'Marks', type: 'float'},
],
belongsTo: 'Semester',
});

Ext.require('Ext.data.*');
Ext.require('Ext.grid.*');


Ext.onReady(function(){
Ext.QuickTips.init();
var store = new Ext.data.Store({
model: "User",

});

store.load({

});

Ext.create('Ext.grid.Panel', {
renderTo: Ext.getBody(),
store: store,
width: 400,
height: 200,
title: 'Application Users',
columns: [
{
text: 'Name',
width: 100,
sortable: false,
hideable: false,
dataIndex: 'email'
},
{
text: 'Email Address',
width: 150,
dataIndex: 'course>aname',

},
{
text: 'Phone Number',
flex: 1,
dataIndex: 'User.course.name'
}
]
});
});

skirtle
22 Aug 2011, 4:19 PM
I don't know of a way to do the nesting using dataIndex: arguably there is no natural way to display the value of a 1-many relationship in a grid cell anyway. I'd have thought it would be quite easy to do it using a custom renderer though:

http://docs.sencha.com/ext-js/4-0/#!/api/Ext.grid.column.Column-cfg-renderer

bodyboarder20
24 Aug 2011, 4:11 AM
Although I agree that displaying 1-to-many data structures in a grid - doesnt make sense - there are times that data on the client side may be encapsulated for organizational purposes....

For example, A [Person] object may contain an embedded [Address] object on the server side (very common).... There is a one to one relationship here... So the JSON may look something like this:



[{
"name": "Jared",
"address": {
"street": "1 Main Street",
"city": "Virginia Beach",
"state": "Virginia"
}
}]


I recognize that you can combat this my using the mapping configuration for a field in a model - however, this forces the data into a different format when returned to the server...

I think the ultimate goal with a web-framework that is aiming to duplicate the object modelling capabilities available on the server side - is to prevent the need to transform the structure of data when being serialized between the client and server side....

I recognize that I am by no means answering the original question here - just a little bit on my thoughts about how ExtJS is currently not setup to handle an "object" field type....

skirtle
24 Aug 2011, 4:58 AM
Agreed. We've had almost exactly this same question asked half a dozen times in this forum over the last few weeks and as far as I can tell there isn't a satisfactory answer at the moment. Support for 1-1 relationships would be ideal, including syntax for using such records in grids and forms.

lucasguaru
24 Aug 2011, 5:14 AM
Hello there!
I resolved this on the field, using the convert function. I used this so I don't loose the sort capability.
I was looking for this and I figured out that the grid doesn't support this.
The code for the store:


Ext.create('Ext.data.Store', {
fields: [
{name:'dt_realizacao'},
{name:'nm_evento'},
{name:'cd_evento', convert:function(value, record) {
return record.get('id').cd_evento;
}
}
]
});

And in the grid, you use the field with convert function instead.
Sorry for the english!

bodyboarder20
24 Aug 2011, 5:23 AM
The convert only works one way - so if you wanted to persist the data back to the server side - it would be in a different form then what you originally received it in.

bodyboarder20
24 Aug 2011, 7:07 AM
Although I'm kind of hijacking this thread by doing so - I worked up an example that will read in lightly nested data - flatten it into the model - and then inflate it again when being written back out by the proxy/writer....

people.json


{
"success": true,
"people": [{
"id": 1,
"name": "Jared",
"address": {
"street": "1 Main Street",
"city": "Virginia Beach",
"state": "Virginia"
}
}]
}


javascript


Ext.override(Ext.data.writer.Json, {

expandObject: function(literal,value) {
var re = /[\[\.]/,
i = String(literal).search(re),
obj;

if(i > 0) {
var split = String(literal).split('.'),
result = this.expandObject(split.slice(1).join('.'), value);
obj = {};
obj[split[0]] = result;
return obj;
} else {
obj = {};
obj[literal] = value;
return obj;
}
},

writeRecords: function(request, data) {
var root = this.root;

if (this.allowSingle && data.length == 1) {
data = data[0];
}

if (this.encode) {
if (root) {
request.params[root] = Ext.encode(data);
}
} else {
request.jsonData = request.jsonData || {};
console.log(this.useSimpleWriter);
if(!this.useSimpleWriter) {
var re = /[\[\.]/,
newData = {};

for (key in data) {
var i = String(key).search(re);
if(i > 0) {
var obj = this.expandObject(key,data[key])
Ext.Object.merge(newData,obj);
} else {
newData[key] = data[key];
}
}
data = newData;
}
if (root) {
request.jsonData[root] = data;
}
else {
request.jsonData = data;
}
}
return request;
}
});

Ext.define('Person',{
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'name'},
// Address is a nested object - flatten the object to the person model
{name: 'address.street', mapping: 'address.street'},
{name: 'address.city', mapping: 'address.city'},
{name: 'address.state', mapping: 'address.state'},
]
});

var personsStore = Ext.create('Ext.data.Store',{
model: 'Person',
proxy: {
type: 'ajax',
api : {
read: 'people.json',
update: 'people.json'
},
reader: {
type: 'json',
root: 'people'
},
writer: {
nameProperty: 'mapping',
useSimpleWriter: false
}
}
});

// After loading the persons - modify the address
// of a person - then sync it back to the server
// and using firebug - inspect the JSON to see
// how it looks...
personsStore.on('load', function(store,records) {
var record = records[0];
record.set('address.street', '12 Alabama Street');
store.sync();
});

// Kick it all off
personsStore.load();


If you run this ( I tested w/ 4.0.2a) and then inspect the post back to the server - you'll see that the address fields are expanded back into the object like when they were read in from the server....

I'm not sure how good this will work in practice - but I plan to go ahead and try it out....

sharikank
26 Mar 2013, 4:50 AM
did it work for you???


Although I'm kind of hijacking this thread by doing so - I worked up an example that will read in lightly nested data - flatten it into the model - and then inflate it again when being written back out by the proxy/writer....

people.json


{
"success": true,
"people": [{
"id": 1,
"name": "Jared",
"address": {
"street": "1 Main Street",
"city": "Virginia Beach",
"state": "Virginia"
}
}]
}


javascript


Ext.override(Ext.data.writer.Json, {

expandObject: function(literal,value) {
var re = /[\[\.]/,
i = String(literal).search(re),
obj;

if(i > 0) {
var split = String(literal).split('.'),
result = this.expandObject(split.slice(1).join('.'), value);
obj = {};
obj[split[0]] = result;
return obj;
} else {
obj = {};
obj[literal] = value;
return obj;
}
},

writeRecords: function(request, data) {
var root = this.root;

if (this.allowSingle && data.length == 1) {
data = data[0];
}

if (this.encode) {
if (root) {
request.params[root] = Ext.encode(data);
}
} else {
request.jsonData = request.jsonData || {};
console.log(this.useSimpleWriter);
if(!this.useSimpleWriter) {
var re = /[\[\.]/,
newData = {};

for (key in data) {
var i = String(key).search(re);
if(i > 0) {
var obj = this.expandObject(key,data[key])
Ext.Object.merge(newData,obj);
} else {
newData[key] = data[key];
}
}
data = newData;
}
if (root) {
request.jsonData[root] = data;
}
else {
request.jsonData = data;
}
}
return request;
}
});

Ext.define('Person',{
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'name'},
// Address is a nested object - flatten the object to the person model
{name: 'address.street', mapping: 'address.street'},
{name: 'address.city', mapping: 'address.city'},
{name: 'address.state', mapping: 'address.state'},
]
});

var personsStore = Ext.create('Ext.data.Store',{
model: 'Person',
proxy: {
type: 'ajax',
api : {
read: 'people.json',
update: 'people.json'
},
reader: {
type: 'json',
root: 'people'
},
writer: {
nameProperty: 'mapping',
useSimpleWriter: false
}
}
});

// After loading the persons - modify the address
// of a person - then sync it back to the server
// and using firebug - inspect the JSON to see
// how it looks...
personsStore.on('load', function(store,records) {
var record = records[0];
record.set('address.street', '12 Alabama Street');
store.sync();
});

// Kick it all off
personsStore.load();


If you run this ( I tested w/ 4.0.2a) and then inspect the post back to the server - you'll see that the address fields are expanded back into the object like when they were read in from the server....

I'm not sure how good this will work in practice - but I plan to go ahead and try it out....