ExtJS 4 writer for nested JSON with array - javascript

I'm trying to handle JSON with nested structure with ExtJS4. Please do not answer like here
because it's wrong answer. I use the expandData: true with model mappings and it works for me really fine.
The problem I expect is with one field that is array of objects. So, here is my code sample:
Ext.define('EdiWebUI.model.Document', {
extend: 'Ext.data.Model',
fields: [
{name: 'document_header_documentReceiveDateTime', mapping: 'document.header.documentReceiveDateTime', type: 'string'},
{name: 'document_header_documentProcessDateTime', mapping: 'document.header.documentProcessDateTime', type: 'string'},
{name: 'document_header_documentID', mapping: 'document.header.documentID', type: 'string'},
...
{name: 'lines', type: 'auto'},
...
{name: 'attachments_documentFile_fileName', mapping: 'attachments.documentFile.fileName', type: 'string'},
{name: 'attachments_documentFile_content', mapping: 'attachments.documentFile.content', type: 'string'}
],
hasMany: [
{model: 'DocumentLines', name: 'lines', associationKey: 'lines'}
],
proxy: {
type: 'rest',
url: '/document',
reader: {
type: 'json',
root: 'data'
},
writer: {
expandData: true,
writeAllFields: true,
nameProperty: 'mapping'
}
}
});
Ext.define('DocumentLines',{
extend: 'Ext.data.Model',
fields: [
{'name': 'line_lineItem_lineNumber', mapping: 'line.lineItem.lineNumber', type: 'string'},
{'name': 'line_lineItem_orderedQuantity', mapping: 'line.lineItem.orderedQuantity', type: 'string'},
{'name': 'line_lineItem_orderedUnitPackSize', mapping: 'line.lineItem.orderedUnitPackSize', type: 'string'},
...
});
So, it working well when reading JSON like this:
{
"data": {
"document": {
"header": {
"documentReceiveDateTime": "2014-03-25T08:34:24",
"documentProcessDateTime": "2014-03-25T08:44:51",
"documentID": "83701540",
...,
"lines": [
{
"line": {
"lineItem": {
"lineNumber": "1",
"orderedQuantity": "5.000",
"orderedUnitPackSize": "1.000"
}
}
},
{
"line": {
"lineItem": {
"lineNumber": "2",
"orderedQuantity": "4.000",
"orderedUnitPackSize": "1.000"
}
}
}
]
...
but I can't make writer to parse lines. When I'm truing to save my document I already have output like this:
{ lines:
[ { line_lineItem_lineNumber: 1,
line_lineItem_ean: '4352345234523',
line_lineItem_orderedQuantity: '45'} ],
(other parts of document are expanded well)
So, here is a question: Is there a way to make it works as I need?
...or I should make a trick on a server side (as I actually do now)...
Thanks in advance.

You have two choices here:
The proper way, which is to use the stores capabilities: define your dataWriter and code your own function in order to get the json you want.
Don't use the store to update your records, create the json you want and use an Ajax request to update the records you need to update.
Both ways uses Ajax anyway, the first one should be preferred.
I would define my writer in the same file as the store, something like:
Ext.define('MyApp.custom.Writer',{
/*
* Formats the data for each record before sending it to the server.
* This method should be overridden to format the data in a way that differs from the default.
*/
getRecordData: function(record) {
var data = {};
/*
* Parse your record and give it whatever structure you need here..
*/
data.lines = [];
return data;
}
});
Although you seem to have one extra level of indirection in your Json, the "lineItem" is not necessarly needed as you already have a one-to-one relationship between line <-> lineItem and lineItem <-> and the object defined by lineItem. But this is a different question.

I've used the answer above, but wanted to share the code to make it a little bit easier for people who try the same thing.
Dr. Leevsey's Code from above worked for me but had the disadvantag that it puts everything inside an array. For my project it worked better if it returned an object (with the child objects) and didn't return an array if the base object is not an array.
Here is the code:
Ext.define('MyApp.util.customWriter',
{
extend: 'Ext.data.writer.Json',
getRecordData: function (record, operation) {
var data = record;
var me = this;
var toObject = function (name, value) {
var o = {};
o[name] = value;
return o;
};
var itemsToObject = function (item) {
for (prop in item) {
if (Array.isArray(item[prop])) {
me.getRecordData(item[prop]);
}
else {
if (item.hasOwnProperty(prop)) {
var nameParts = prop.split('.');
var j = nameParts.length - 1;
if (j > 0) {
var tempObj = item[prop];
for (; j > 0; j--) {
tempObj = me.toObject(nameParts[j], tempObj);
}
item[nameParts[0]] = item[nameParts[0]] || {};
Ext.Object.merge(item[nameParts[0]], tempObj);
delete item[prop];
}
}
}
}
};
if (!Array.isArray(data)) {
data = data.getData();
itemsToObject(data);
}
else {
var dataLength = data.length;
for (var i = 0; i < dataLength; i++) {
itemsToObject(data[i]);
}
}
return data;
}
});

Related

Declaring dynamic keys in json (javascript, node)

I have an array of objects, each object is similar to:
{ word: 'intentional',
definition: 'done by intention or design',
type: 'adjective',
Synonyms: [ 'conscious', 'deliberate', 'intended', 'knowing', ] }
I am trying to convert the whole array into following json format:
{
"conscious": {
"data": ["done by intention or design"],
"type": "adjective",
"Synonym For": ["intentional"]
},
"deliberate": {
"data": ["done by intention or design"],
"type": "adjective",
"Synonym For": ["intentional"]
},
...
}
This json format is an input to another program, which I do not control.
I am running it on node.js.
How can I declare an object and then loop through the array to fill it as intended?
var obj = { word: 'intentional',
definition: 'done by intention or design',
type: 'adjective',
Synonyms: [ 'conscious', 'deliberate', 'intended', 'knowing' ] },
res = obj.Synonyms.reduce(function(s,a) {
s[a] = { data: [obj.definition], type: obj.type, SynonymFor: [obj.word] };
return s;
}, {});
console.log(res);
var jsonObj = {};
wordArray.forEach((word) => {
word.Synonyms.forEach((synonym) => {
jsonObj[synonym] = {
data: [word.definition],
type: word.type,
'Synonym For': [word.word]
};
})
})

How to map model to table when the structure is array based?

I have my data as following:
{
meta: {
format: "csv",
info: "desc",
columns: [
{
id: "Name",
type: "Text",
length: 32
},
{
id: "Text",
type: "Text",
length: 128
}]
},
rows: [
["John","xxxx"],
["Alpha","yyyy"],
["Beta","wwww"],
["Gamma","zzzz"]]
}
Now, I am struggling to map the records to a Table control as Columns and Rows. Column seems straight forward, straight map, but the rows since lacks a mapping to column I wonder what could be the simplest way.
Approach Steps:
Make a keys[] from column.id of each columns record.
Traverse the rows[]
Each loop, while keys.length create an object as {keys[j]:row[k]}
Push to an array
Recreate the original JSON but replace Rows arrays with Objects
I am really struggling to translate this into code specially at the rows[] parsing and creating Objects. Is there, I am sure there must be, an efficient way to achieve this.
Here is what you could do. using Array.map and forEach.
var input = {
meta: {
format: "csv",
info: "desc",
columns: [{
id: "Name",
type: "Text",
length: 32
}, {
id: "Text",
type: "Text",
length: 128
}]
},
rows: [
["John", "xxxx"],
["Alpha", "yyyy"],
["Beta", "wwww"],
["Gamma", "zzzz"]
]
};
var columns = input.meta.columns.map((column) => {
return column.id
});
var rows = input.rows.map((row) => {
var obj = {};
row.forEach((column, idx) => {
obj[columns[idx]] = column;
});
return obj;
});
input.rows = rows;
console.log(input);

Nested Javascript Object : recursion and "complex" transformation (with lodash)

I apologize in advance for the complex example here; I tried to trim it down as much as I could to illustrate what I try to achieve
I have a complex structure that I need to traverse and transform based on some conditions; Here's an (short) example of the structure that should cover most scenarios:
{ PROP1: {
metadata: Object, // somewhere deeper in metadata I have a `value` key
parent: { $ref: String },
employee: {
parent: { $ref: String },
id: String,
metadata: Object,
products: {
metadata: Object,
model: { $ref: String },
type: 'array',
value: ["String", "String" , "String"] ,
[...]
},
model: {
id: String,
email: {
metadata: Object,
value: 'a#b.com',
type: 'string',
validity: Object,
[...]
},
name: {
firstName: {
metadata: Object,
value: 'John',
type: String,
validity: Object,
[...]
},
lastName: {
metadata: Object,
value: 'Smith',
type: String,
validity: Object,
[...]
},
}
},
operations: {
id: String,
items: [
{ action: {value: "UPDATE", model: {$ref: String }, [...] },
{...}
],
metadata: Object,
[...]
}
}
},
PROP2: {
// similar as PROP1
},
[... and so on ...]
}
I basically need to clean that up before sending it to the backend;
Whenever a value contains $ref, I don't want the key/val pair (e.g.: PROP1.parent is of no use and can be omitted)
whenever a value contains value, I need to omit everything else and move the value of value as the value of key (e.g.: PROP1.employee.products should equal ['String', 'String', 'String'])
keys like id, metadata, validity (etc) can be completely omitted regardless of its content
So the end result should be along those lines:
{ PROP1: {
employee: {
products: ['item','item','item'],
model: {
email: 'a#b.com',
name: { firstName: 'John', lastName: 'Smith'},
},
operations: [
{action: 'UPDATE'}
]
}
},
PROP2: { ... }
}
I tried lots of different approaches using different lodash methods but couldn't wrap my head around this...
Any help will be greatly appreciated
Thanks
In pseudo code, try something like this. Implement the specifics and post more info when you run into trouble.
var ignoreKeyArray = ["id", ...] // keys to ignore and remove
var newJSON = "{}";
for (property in JSON) {
var newProp = parseJSON(key, property);
insert newProp in newJSON object
}
var parseJSON = function (key, jsonBlob) {
if (key in ignoreKeyArray || jsonBlob contains value "$ref")
return "";
var jsonOut = key + ": {}";
for (child in jsonBlob) {
add parseJSON(child) to jsonOut;
}
return jsonOut;
}
If you have any questions, comment so I can extend the answer and clarify.

How to loop over an array within a JavaScript object?

Very new to JavaScript objects, so I am not to sure how to go about doing this.
Within my object, I have an array called myArray. I am attempting to loop over it to print out everything on the page. Usually there is a a lot more data within the object, but it has been removed for this example.
This is my object:
var data = [
{
myArray:
{
name: 'name1',
code: 'code1',
data: {
date: '20-Apr-2014',
signal: 'signal1'
}
},
{
name: 'name2',
code: 'code2',
data: {
date: '21-Apr-2014',
signal: 'signal2'
}
}
}
]
This is my iteration code:
var arrayLength = data.myArray.length - 1;
for (var i = 0; i <= arrayLength; i++) {
var name = data.myArray[i].name;
console.log(name);
}
My code above should produce the results in the console name1 and name2. However, I am getting an error of Cannot read property 'length' of undefined.
How can I change my above code to do this?
Your object should use brackets for the array:
var data = {
myArray: [
{
name: 'name1',
code: 'code1',
data: {
date: '20-Apr-2014',
signal: 'signal1'
}
},
{
name: 'name2',
code: 'code2',
data: {
date: '21-Apr-2014',
signal: 'signal2'
}
}
]
}
I've also removed the outermost brackets, since it would appear from your question that your intent was to have a single array inside an object, and not an array of arrays.
With the object above, your iteration code will work fine.

dynamic columnDef in ng-Grid

I want to assign ng-grid columns name dynamically after value returned from database, but issue is that it get initialized before data return from ajax, and i am not able to recall gridOption so it showing balnk, so please help me that how can we construct a column name by ajax return value.
$scope.gridOptions =
{
data: 'data.Values',
columnDefs:
[
{ field: "ID", displayName: "Record Id" },
{ field: "Value", displayName: $scope.ColumnName, cellFilter: cellfilterType },
],
};
where $scope.ColumnName coming from below line...
RecordService.getRecords().then(function (data) {
$scope.ColumnName= data.something;
}
Thanks
Thanks Max for your help, I have done this with of help columnDef as below:
Step 1:
$scope.colDef = [];
Step 2:
RecordService.getRecords().then(function (data){
$scope.colDef=["ColumnName":data.something]
}
Step 3:
$scope.gridOptions = {
data: 'data.UdiValues',
columnDefs:'colDef',
filterOptions: $scope.filterOptions
};
Try to set first "default" value and after change it with promise
$scope.gridOptions =
{
data: 'data.Values',
columnDefs:
[
{
field: "ID",
displayName: "Record Id"
},
{ field: "Value",
displayName: "default",
cellFilter: cellfilterType
},
]
};
And now:
RecordService.getRecords().then(function (data) {
$scope.gridOptions.columnDefs[1].displayName = data.something;
}
The Service RecordService returns promise therefore we create promise factory like:
.factory('RecordService', ['$resource','$q', function($resource, $q) {
var data = { something: "from service" } ;
var factory = {
getRecords: function (selectedSubject) {
var deferred = $q.defer();
deferred.resolve(data);
return deferred.promise;
}
}
return factory;
}]);
Demo Fiddle
Something like this
Return json, play around, use GET mapping to strings etc?
i have done something like this :-
self.gridOptions.columnDefs = columnDefs(colDef,displayNames);
columnDef is :-
var columnDefs = function(data,cd){
var colDef= [];
var mi = null;
var colByOrder = sortedByOrder(data);
for(var i=0 ; i < colByOrder.length ; i++){
colDef.push({
width: width,
field: String(colByOrder[i][1].position),
menuItems: menuItems(this),
displayName: cd[colByOrder[i][1].position],
enableSorting: false,
type: 'string',
});
}
return colDef;
};

Categories

Resources