PDA

View Full Version : Editing nested array in Json Store problem



jackygurui
5 Mar 2010, 6:35 AM
I've been trying to use the demo as an example to create a grid which reads data from a JSON RESTful service and post update values back. I am stuck with editing the nested array problem. I've read some threads from this forum but still can't solve my problem.

I've got a nested array like this,



{
"company":
{
"name":"company1",
"number":123,
"id":1,
"version":1,
"addressRef":{
"address1":"Line 1",
"address2":"Line 2",
"address3":"Line 3",
"city":"city",
"comment":"comment",
"county":"country",
"email":"email",
"fax":"fax",
"id":1,
"postcode":"postcode",
"telephone":"tel",
"town":"town",
"version":0
}
}
"message":"OK",
"total":1,
"success":true
}


I've created a JsonReader which looks like this



var creader = new Ext.data.JsonReader({
totalProperty: 'total',
successProperty: 'success',
idProperty: 'id',
root: 'company',
messageProperty: 'message',
"sortInfo":{
"direction":"ASC",
"field":"id"
}
}, [
{
name: 'name',
allowBlank: false
}, {
name: 'number',
allowBlank: false
}, {
name: 'id'
}, {
name: 'version'
}, {
name: 'address1',
allowBlank: false,
mapping: 'addressRef.address1'
}, {
name: 'address2',
mapping: 'addressRef.address2'
}, {
name: 'address3',
mapping: 'addressRef.address3'
}, {
name: 'town',
mapping: 'addressRef.address4'
}, {
name: 'city',
mapping: 'addressRef.city'
}, {
name: 'county',
mapping: 'addressRef.county'
}, {
name: 'postcode',
mapping: 'addressRef.postcode'
}, {
name: 'id',
mapping: 'addressRef.id'
}, {
name: 'telephone',
mapping: 'addressRef.telephone'
}, {
name: 'fax',
mapping: 'addressRef.fax'
}, {
name: 'email',
mapping: 'addressRef.email'
}, {
name: 'comment',
mapping: 'addressRef.comment'
}, {
name: 'version',
mapping: 'addressRef.version'
}
]);


Then in the GridPanel, i've created some columns


var ccolumns = [
new Ext.grid.RowNumberer(),
{
header: "Name",
width: 100,
sortable: true,
dataIndex: 'name',
editor: new Ext.form.TextField({})
},
{
header: "Number",
width: 100,
sortable: true,
dataIndex: 'number',
editor: new Ext.form.TextField({})
},
{
header: "Line 1",
width: 100,
sortable: true,
dataIndex: 'address1',
editor: new Ext.form.TextField({})
},
{
header: "Line 2",
width: 100,
sortable: true,
dataIndex: 'address2',
editor: new Ext.form.TextField({})
},
{
header: "Line 3",
width: 100,
sortable: true,
dataIndex: 'address3',
editor: new Ext.form.TextField({})
},
{
header: "Town",
width: 100,
sortable: true,
dataIndex: 'town',
editor: new Ext.form.TextField({})
},
{
header: "City",
width: 100,
sortable: true,
dataIndex: 'city',
editor: new Ext.form.TextField({})
},
{
header: "County",
width: 100,
sortable: true,
dataIndex: 'county',
editor: new Ext.form.TextField({})
},
{
header: "Postcode",
width: 100,
sortable: true,
dataIndex: 'postcode',
editor: new Ext.form.TextField({})
},
{
header: "Telephone",
width: 100,
sortable: true,
dataIndex: 'telephone',
editor: new Ext.form.TextField({})
},
{
header: "Fax",
width: 100,
sortable: true,
dataIndex: 'fax',
editor: new Ext.form.TextField({})
},
{
header: "Email",
width: 100,
sortable: true,
dataIndex: 'email',
editor: new Ext.form.TextField({})
},
{
header: "Comment",
width: 150,
sortable: true,
dataIndex: 'comment',
editor: new Ext.form.TextField({})
}
];

var companyGrid = new Ext.grid.GridPanel({
iconCls: 'icon-grid',
border: false,
autoScroll: true,
height: 300,
store: store,
plugins: [editor],
columns : ccolumns,
tbar: [{
iconCls: 'icon-add',
text: 'New',
handler: onAdd
}, '-', {
iconCls: 'icon-delete',
text: 'Delete',
handler: onDelete
}, '-'],
viewConfig: {
forceFit: true
}
});


Problem is when I edit the record and send back to the server, I get a piece of data with different format which looks like this, and of course it is not expected by the server.



{
"company":
{
"name":"company1 modified",
"number":123,
"id":1,
"version":1,
"addressRef.address1":"Line 1 modified",
"addressRef.address2":"Line 2",
"addressRef.address3":"Line 3",
"addressRef.city":"city",
"addressRef.comment":"comment",
"addressRef.county":"country",
"addressRef.email":"email",
"addressRef.fax":"fax",
"addressRef.id":1,
"addressRef.postcode":"postcode",
"addressRef.telephone":"tel",
"addressRef.town":"town",
"addressRef.version":0
}
}


Is there anyway to get the JSON Writer write data in the nested array instead of writing to the root array using mapped names??

Cheers

jackygurui
6 Mar 2010, 3:37 AM
Bump up.


Is there any alternative solutions?

ZeusTheTrueGod
6 Mar 2010, 10:35 PM
Yeah, you should insert a hook into your JsonWriter instance with createSequence/createInterceptor or other way of JsonWriter overriding.

use firebug, and find a place where DataWriter.toHash is called.

then your task is convert object
{
"company":
{
"name":"company1 modified",
"number":123,
"id":1,
"version":1,
"addressRef.address1":"Line 1 modified",
"addressRef.address2":"Line 2",
"addressRef.address3":"Line 3",
"addressRef.city":"city",
"addressRef.comment":"comment",
"addressRef.county":"country",
"addressRef.email":"email",
"addressRef.fax":"fax",
"addressRef.id":1,
"addressRef.postcode":"postcode",
"addressRef.telephone":"tel",
"addressRef.town":"town",
"addressRef.version":0
}
}
to your format.

jackygurui
7 Mar 2010, 4:48 AM
Sounds very complicate, when there are a lot of those nested arrays.

ZeusTheTrueGod
7 Mar 2010, 7:12 AM
Sounds very complicate, when there are a lot of those nested arrays.

Is it realy complicated? Just a recursion, clone algorithm with a hook on nested properties.

you have to make a deep copy of one object to another for all properties but those which are like 'a.b.c.d.e.....' . For those one you should create a hierarchy if it was not created before. This is a one hour job with all arrays.

jackygurui
7 Mar 2010, 2:24 PM
I've tried another approach like this:



var addressRecord = Ext.data.Record.create([
{
name: 'address1',
allowBlank: false
},
{
name: 'address2'
},
{
name: 'address3'
},
{
name: 'town'
},
{
name: 'city'
},
{
name: 'county'
},
{
name: 'postcode'
},
{
name: 'id'
},
{
name: 'telephone'
},
{
name: 'fax'
},
{
name: 'email'
},
{
name: 'comment'
},
{
name: 'version'
}]);

var companyRecord = Ext.data.Record.create([
{
name: 'name',
allowBlank: false
},
{
name: 'number',
allowBlank: false
},
{
name: 'id'
},
{
name: 'version'
},{
name: 'addressRef',
type: addressRecord
}
]);

var reader = new Ext.data.JsonReader({
totalProperty: 'total',
successProperty: 'success',
idProperty: 'id',
root: 'company',
messageProperty: 'message',
"sortInfo":{
"direction":"ASC",
"field":"id"
}
}, companyRecord);

var writer = new Ext.data.JsonWriter({
encode: false,
writeAllFields: true
});

var store = new Ext.data.Store({
id: 'user',
restful: true,
proxy: proxy,
reader: reader,
writer: writer
});


It turns out that the writer can write to the RESTful service in correct format, however, the grid cann't display the nested data, also the data received at the server side shows only arrays root fields been updated, the nested array is untouched.


So, I think there are two problems/bugs here:

1. The writer can't write to nested array instead it writes to root array, meanwhile the editor gets the nested fields fine by creating a mapping to the nested fields.
2. The editor in the column model can't access nested array, that's why I have to map the nested field to make it accessible to the editor.

Mike Robinson
8 Mar 2010, 7:58 AM
You're probably going to have to write your own AJAX calls ... or, at minimum, your own host-side handling routines that can understand what ExtJS is saying to them.

I would counsel that you ought not to "do anything too clever," because you will surely regret it. In other words, while you might find something that works, you've also got to maintain it. If the next complication in your data implies another complication in your solution... you're definitely barking up the wrong tree.

Condor
9 Mar 2010, 12:00 AM
How are you sending the data back to the server? Did you build the data yourself or did you use a JsonWriter?

jackygurui
9 Mar 2010, 1:26 AM
How are you sending the data back to the server? Did you build the data yourself or did you use a JsonWriter?

I'm using JsonWriter like this:


var cwriter = new Ext.data.JsonWriter({
encode: false,
writeAllFields: true
});

jackygurui
9 Mar 2010, 1:28 AM
You're probably going to have to write your own AJAX calls ... or, at minimum, your own host-side handling routines that can understand what ExtJS is saying to them.

I would counsel that you ought not to "do anything too clever," because you will surely regret it. In other words, while you might find something that works, you've also got to maintain it. If the next complication in your data implies another complication in your solution... you're definitely barking up the wrong tree.

Normally, you'd thought the library should have no problem of handling complicate data structures since it has been out for so long and thousands of big famous companies are using it.

Condor
9 Mar 2010, 1:58 AM
You'll have to write your own render method for the JsonWriter, because the default implementation doesn't work for you.

terminal8
15 Mar 2010, 6:56 AM
I second to this. JsonWriter should be aware about nested records and submit them correctly. This is deadly easy in plain HTML, and I think it should be easy in ExtJS too :-)

Mike Robinson
15 Mar 2010, 9:09 AM
I'm not here to defend their implementation. But I would point out that the data structure of a Store object is: a flat list of identical records. Just like a garden-variety database table, ExtJS has no concept of "one-to-many relationships" within a single Store.

While it might be very common for some persistent business object (e.g. an Invoice with its line-items) to have complex relationships ... or to see those in an "Object Relational Mapper (ORM)" ... the ExtJS "Store" isn't an ORM. It's a GUI toolkit with reasonable "smarts."

Ultimately, you'll have to deal with this problem on both the client and the host sides. There are many ways to do it, depending on your application.

From a design standpoint, "one" is easy to handle in a general case, but "one-to-many" or "many-to-many" is not. Yet, if you provide a good solid implementation of "one," the client's additional code can support the "many." Yes, you have to write it, but that's pretty much the case with any computer application: "we provide the Erector set; you build the Brooklyn Bridge."

Ranma13
20 Mar 2010, 6:15 AM
Nobody's asking ExtJS to be an ORM, we're talking about nested JSON. The two are completely different concepts. Is it so unrealistic to expect a JsonStore to be able to keep the nesting intact? To force users to flatten the JSON defeats one of the primary advantages of using JSON (preserving object hierarchy).

ran.davidovitz
30 Apr 2010, 10:42 AM
i don't understand how this was not handled until now?
the issues of nested array - or might i say true object is a valid scenario that every application has, i would expect a valid nativ solution from ExtJS and not each person investing the wheels

Mike Robinson
30 Apr 2010, 11:29 AM
The market-opportunity awaits you ... thousands of anxious fans can't wait to send you their zero-dollar bills. 8-|

The implementations that exist in the ExtJS objects are not "the be-all and end-all of what can be done."

marcing
8 Jan 2011, 3:24 AM
I am stuck at this issue, is it really that difficult to fix that? I just want an option at JsonWriter to send nested Json instead stupid "." notation.

It took me one hour just to find it's impossible in ExtJS. I keep losing time on this stupid things.

Condor
8 Jan 2011, 3:44 AM
Why impossible? You only have to write your own render method (as I already said in post #11). That's not too difficult, is it?

marcing
26 Sep 2011, 7:17 AM
I meant it is impossible with default implementation in ExtJS. My opinion is that it should be, or more customers will be angry - just like me.

portal
10 Oct 2011, 6:54 AM
// The new DataWriter component.
var MyWriter = Ext.extend(Ext.data.JsonWriter, {
doNestedJSON : true,
constructNestedJSON : function(data) {
if (data instanceof Array) {
for (var key in data) {
this.constructNestedJSON(data[key]);
}
} else {
for (var key in data) {
if (data.hasOwnProperty(key))
if (key.indexOf('.') > 0) {
var split = key.split(".");
var mySize = split.length - 1;

if (typeof data[split[0]] === "undefined")
data[split[0]] = new Object();

var tmpData = data[split[0]];

for (i = 1; i <= mySize; i++) {
if (i < mySize) {
if (typeof tmpData[split[i]] === "undefined") {
tmpData[split[i]] = new Object();
}
tmpData = tmpData[split[i]];

} else {
tmpData[split[i]] = data[key];
}
}
delete data[key];
}

}
}
return Ext.util.JSON.encode(data);

}
});

var writer = new MyWriter({
render : function(params, baseParams, data) {
if (this.encode === true) {
Ext.apply(params, baseParams);
if (this.doNestedJSON === true) {
var myJsonObject = Ext.util.JSON.decode(Ext
.encode(data));
params[this.meta.root] = this
.constructNestedJSON(myJsonObject);
} else {
// Encode here now.
params[this.meta.root] = Ext.encode(data);
}
} else {
// defer encoding for some other layer, probably in
// {@link Ext.Ajax#request}. Place everything into
// "jsonData" key.
var jdata = Ext.apply({}, baseParams);
jdata[this.meta.root] = data;
params.jsonData = jdata;
}
},

encode : true,
writeAllFields : false
});



This is what I have done to suite my needs of nested JSON, which will be accepted "as is" from the server-side JSON data binding API.

I know that this is not a general js or algorithm forum, but any comment on the implementation is welcome, since I am definitely not alg/js guru.

Here is the Record(dummy for testing):

var Firm = Ext.data.Record.create([
{name: 'id'},
{
name: 'name',
type: 'string'
},{
name: 'description',
type: 'string'
},{
name: 'active',
type: 'boolean'
},
{name: 'value', mapping:'labelValue.value'}
,
{name: 'label', mapping:'labelValue.label'}
,
{name: 'innerFirmName', mapping:'ceAccount.name'}
,
{name: 'innerFirmNameLabel', mapping:'ceAccount.labelValue.label'}
]);

and the output


[

{"name":"Firm Name2","description":"Descriptoin here...2","active":"true","labelValue":{"value":"test2","label":"false"},"ceAccount":{"name":"3333","labelValue":{"label":"4444"}}},

{"name":"Firm Name","description":"Descriptoin here...","active":"true","labelValue":{"value":"test1","label":"false"},"ceAccount":{"name":"1111","labelValue":{"label":"222"}}}

]

lschvent
1 Apr 2014, 12:00 PM
I got the same problem.
I can edit or send data correctly but not both.

I did write a render method but it's not normal that extjs doesnt handle this natively.
Its so common to have a graph of object where you need to edit all nodes...

I managed to write a correct json but it was an easy scenario where i had only

class A {
string a1;
etc...
B b;
}

If i got a more complex structure im not really sure to be able to do it on time because Projects dont wait.
you got to be fast when you got deadlines and that's why you use a framework : to speed up your dev.

Sencha team can you please post an implementation of a render method for a JsonWriter wich would transform dot notation to a normal json with nested fields.
Or a simple way to make a field points to a nested record field.