Loading nested JSON array with knockout js - javascript

I am trying to load a form where I can update the existing data from the database.
My Object has one nested array in inside each element in that array also have a nested array.
It looks like the following
{
"Id": 13,
"Name": "Category 1",
"DeliveryOptions":
[
{
"Id": 15,
"DeliveryCategoryId": 13,
"Name": "Option 1.-1",
"DeliveryBreakPoints":
[
{
"Id": 19,
"DeliveryOptionId": 15,
"Start": 0,
"End": 10,
"Flat": 1,
"Variable": 2,
"Delete": false
}
]
},
{
"Id": 16,
"DeliveryCategoryId": 13,
"Name": "Option1-2",
"DeliveryBreakPoints":
[
{
"Id": 20,
"DeliveryOptionId": 16,
"Start": 0,
"End": null,
"Flat": 1,
"Variable": 3,
"Delete": false
}
]
}
]
}
You can see inside the object there's an array called "DeliveryOptions" and each "DeliveryOptions" array has another array called "DeliveryBreakPoints"
I have configured knockout js model as the following
function DeliveryCategory(id, name, options) {
this.Id = id;
this.Name = name;
this.DeliveryOptions = ko.observableArray(options);
}
function DeliveryOption(id, name, breakpoint) {
bpIdCounter = 0;
this.Id = id;
this.Name = name;
this.DeliveryBreakPoints = ko.observableArray(breakpoint);
this.addDeliveryBreakPoint = function (option) {
option.DeliveryBreakPoints.push(new DeliveryBreakPoint(bpIdCounter++));
}
}
function DeliveryBreakPoint(id, start, end, flat, variable) {
var self = this;
self.Id = id;
self.Start = start;
self.End = end;
self.Flat = flat;
self.Variable = variable;
}
I am passing parameters (such as id and name ...) as this code is also used to create new object.. However I am not sure if this is preventing it to get the existing model binded.
I also created a view model which looks like this
function DeliveryCategoryViewModel(model) {
var self = this;
if (model.Id > 0) {
self.DeliveryCategory = ko.observable(model);
} else {
self.DeliveryCategory = ko.observable(new DeliveryCategory(null, "", [new DeliveryOption(null, "")]));
}
self.addDeliveryOption = function () {
self.DeliveryCategory().DeliveryOptions.push(new DeliveryOption(null, "", [new DeliveryBreakPoint(null,"0","","","","")]));
}
self.removeDeliveryOption = function (option) {
self.DeliveryCategory().DeliveryOptions.remove(option);
}
self.removeDeliveryBreakPoint = function (option, breakPoint) {
option.DeliveryBreakPoints.remove(breakPoint);
}
self.onSubmit = function () {
$.ajax({
url: "CreateDeliveryCategory",
type: "POST",
data: ko.toJSON(self),
async: true,
contentType: "application/json"
}).success(function (data) {
window.location = data;
}).error(function(error){
alert(error);
})
}
}
This code is also used when I create a new delivery category from the scratch which works fine(this is why there is a check for "model.Id > 0"). It doesn't bind the model if model.id > 0. It only displays Delivery Category name and the first Delivery Option name correctly(even there are two delivery options) and the rest is just broken.
Can anyone please pinpoint what is going on?

Related

Create array of objects from ajax result

I am using DataTables with ajax and I am trying to dynamically generate a columns list as in the example here: https://datatables.net/examples/ajax/orthogonal-data.html (only in my case I have display and order for all items).
I tried with this approach, but this doesn't work and I am guessing push my sub values wrong.
Input (ajax result):
[{
"itemId": {
"order": "BG007002",
"display": "BG007002"
},
"builtDate": {
"order": "2000-03-01",
"display": "01.03.2000"
},
"openedDate": {
"order": "2005-07-09",
"display": "09.07.2005"
},
"buildingSize": {
"order": 15000,
"display": "15.000"
}
}, // ...
Expected output:
[
{ data: {
_: "itemId.display",
sort: "itemId.order"
}
},
{ data: {
_: "builtDate.display",
sort: "builtDate.order"
}
},
{ data: {
_: "openedDate.display",
sort: "openedDate.order"
}
},
{ data: {
_: "buildingSize.display",
sort: "buildingSize.order"
}
}
]
My approach:
var reportColsShort = $('#reportColsShort').text().slice(0,-1);
var aoCols = [];
var colss = reportColsShort.split(',');
for (var i = 0; i < reportColsShort.split(',').length; i++) {
var aoColss = {};
aoColss['data']['_'] = colss[i].display;
aoColss['data']['sort'] = colss[i].order;
aoCols.push(aoColss); // expected output
}
Error:
Cannot set property '_' of undefined.
Update:
Here is one more reference to what I am trying to achieve:
https://datatables.net/reference/option/columns.render#Examples
You can take the ajax result and then use a map see docs to structure your data like this:
const data = [{
"itemId": {
"order": "BG007002",
"display": "BG007002"
},
"builtDate": {
"order": "2000-03-01",
"display": "01.03.2000"
},
"openedDate": {
"order": "2005-07-09",
"display": "09.07.2005"
},
"buildingSize": {
"order": 15000,
"display": "15.000"
}
}];
const mapResult = data.map((elem) => {
let array = [];
Object.keys(elem).forEach((key) => {
const temp = {
data: {
_: elem[key].display,
sort: elem[key].order
}
}
array.push(temp);
});
return array;
});
console.log(mapResult);

Creating Tableau WDC from associative array

I am creating a Tableau Web Data Connector as described in their Getting Started guide HERE.
I have implemented a similar solution previous with data from a basic associative array, but I am having trouble with my current API call.
I make an API call to an external web service that returns a JSON similar to the one listed below (simplified version)(COMMENT simply added for clarity and not part of original code).
{
"status": true,
"employee": {
"id": 123
},
"company": {
"id": 123
},
"job": {
"id": 123,
"job_workflows": [{
"id": 1,
"name": "Start",
"action_value_entered": "Start"
}, {
"id": 2,
"name": "Date",
"action_value_entered": "2017-09-11"
}, {
"id": 3,
"name": "Crew",
"action_value_entered": "Crew 3"
},
**COMMENT** - 17 other unique fields - omitted for brevity
]
}
}
For my requirements, I need to create a new column for each of my JSON job_workflows to display the data in Tableau. I.e.
Column 1 Header = Start
Column 1 value = Start
Column 2 Header = Date Complete
Column 2 value = 2017-09-11
Etc.
My Tableau JavaScript file looks as below:
(function () {
var myConnector = tableau.makeConnector();
myConnector.init = function(initCallback) {
initCallback();
tableau.submit();
};
myConnector.getSchema = function (schemaCallback) {
var cols = [
{ id : "start", alias : "Start", dataType: tableau.dataTypeEnum.string },
{ id : "date", alias : "Date", dataType: tableau.dataTypeEnum.datetime },
{ id : "crew", alias : "Crew", dataType: tableau.dataTypeEnum.string }
];
var tableInfo = {
id : "myData",
alias : "My Data",
columns : cols
};
schemaCallback([tableInfo]);
};
myConnector.getData = function (table, doneCallback) {
$.getJSON("http://some/api/call/data.php", function(response) {
var resp = response; // Response is JSON as described above
var tableData = [];
// Iterate over the JSON object
for (var i = 0, len = feat.length; i < len; i++) {
// not working
tableData.push({
"start": resp.job.job_workflows[i].action_value_entered,
"date": resp.job.job_workflows[i].action_value_entered,
"crew": resp.job.job_workflows[i].action_value_entered
});
}
table.appendRows(tableData);
doneCallback();
});
};
tableau.registerConnector(myConnector);
})();
How do I iterate over the JSON job_workflow and assign each action_value_entered to be a id in my getSchema() function? My current version simply hangs and no values are returned. NO error or warnings thrown.

Javascript strange behavior in Json Object Manipulation

I really can't get my hand around what is happening there and I really think that I should share this with you guys:
I am making a call to my api at '/products' and the response looks something like this in postman:
[
{
"id": 2,
"name": "some_product_name",
"description": "description",
"price": "$120.00",
"firmware_version": "1.0",
"quantity_in_stock": 50,
"selling": true,
"build_version": "1.2",
"category_id": 1,
"available_colors": [
{
"name": "blue",
"in_stock": true,
"picture": {
"type": "Buffer",
"data": []
}
},
{
"name": "black",
"in_stock": true,
"picture": null
},
{
"name": "silver",
"in_stock": true,
"picture": {
"type": "Buffer",
"data": []
}
}
]
}
]
Now what I am trying to do here is to create a new object called products_showcase that has one entry per product color, with the same informations except for the available_colors property, replaced by the color object:
$scope.initModel = function() {
$http({
method: 'GET',
url: '/products'
}).then(function(resp) {
console.log(resp.data);
$scope.products = resp.data;
$scope.products.forEach((item, index, array) => {
item.available_colors.forEach((color, index, array) => {
var product = item;
product.color = {};
product.color = color;
delete product['available_colors'];
$scope.products_showcase.push(product);
});
});
}, function(error) {
$scope.error = error;
});
};
But then, something really strange is happening:
The available_colors property gets deleted also in the response object, that I did not touch in the code in any way.
The color property gets added to the response object too.
The products_showcase is an array containing the same object 3 times with the color property equal to the firs color of the $scope.products.available_colors object i am iterating through
Javascript won't create new object when you assign it to a variable. You can use Object.create function to create a new object
from existing one.
$scope.initModel = function() {
$http({
method: 'GET',
url: '/products'
}).then(function(resp) {
console.log(resp.data);
$scope.products = Object.create(resp.data);
$scope.products.forEach((item, index, array) => {
item.available_colors.forEach((color, index, array) => {
var product = Object.create(item);
product.color = {};
product.color = color;
delete product['available_colors'];
$scope.products_showcase.push(product);
});
});
}, function(error) {
$scope.error = error;
});
};
Javascript assignment operator doesn't apparently create a new instance of the object for the new variable, it simply creates a pointer to the memory address of the 'father' object.
Example code:
var object = {property: 'myProp'};
console.log(object);
{property: 'myProp'}
var newObj = object;
newObj.newProperty = 'myNewProp';
console.log(object);
{property: 'myProp', newProperty: 'myNewProp'}
To create a new instance of the object we have to use the Object.create() method.

How to iterate over json with root node in JavaScript

I have a json with root node:
{
"comments":
[
{"id":1,"author_name":null,"comment_text":null,"url":"http://localhost:3000/comments/1.json"}
]
}
and I want to go through each object.
This is what i try:
var commentNodes = this.props.comments.map(function(comment,index){
// do something over each object
);
});
But it is not working:
Uncaught TypeError: this.props.comments.map is not a function
You can just iterate over it with a for loop like this:
data = {
"comments": [
{
"id": 1,
"author_name": null,
"comment_text": null,
"url": "http://localhost:3000/comments/1.json"
}, {
"id": 2,
"author_name": null,
"comment_text": null,
"url": "http://localhost:3000/comments/2.json"
}
]
}
for (var i = 0; i < data.comments.length; i++) {
var comment = data.comments[i];
// do something with the comment
// console.log(comment.id);
// console.log(comment.url);
}
Or if you want to get all comment authors for example, you could do this:
var authors = data.comments.map(function(comment) {
return comment.author_name;
});

How to bind an array to a ListView

I know how to parse with Json and bind a file like this to a listview:
[
{
"key": "Arthur Schopenhauer",
"numeroFrasi": 3,
"foto" : "images/TEST.jpg",
},
{
"key": "Nietzsche",
"numeroFrasi": 1,
"foto" : "images/TEST.jpg",
},
.........
But I can't understand nor find on Web how to bind just every "frasi" (that is an array) in a file like this:
[
{
"key": "Arthur Schopenhauer",
"numeroFrasi": 3,
"foto" : "images/TEST.jpg",
"frasi": [
"Fras1",
"Frase 2 schopenahuer",
"Frase 3 schopenhahuer"
]
},
{
"key": "Nietzsche",
"numeroFrasi": 1,
"foto" : "images/TEST.jpg",
"frasi": [
"Frase 2 nietzsche",
"Frase 3 nietzsche"
]
},
...............
My Object isn't an array, but it is definied like this form a txt file parsed with Json:
This is the generic definition:
(function () {
"use strict";
var list = new WinJS.Binding.List();
var groupedItems = list.createGrouped(
function groupKeySelector(item) { return item.group.key; },
function groupDataSelector(item) { return item.group; }
);
WinJS.xhr({ url: "/data/frasi.txt" }).then(function (xhr) {
var items = JSON.parse(xhr.responseText);
// Add the items to the WinJS.Binding.List
items.forEach(function (item) {
list.push(item);
});
});
Then this is the specific definition (because when I navigate to my page, I select just an "item", so only one "key, "numeriFrasi", "foto", "frasi":
WinJS.UI.Pages.define("/pages/itemDetail/itemDetail.html", {
ready: function (element, options) {
item = options && options.item ? Data.resolveItemReference(options.item) : Data.items.getAt(0);
"resolveItemReference" gets one item from all the items created
Strip out frasis to an Array first. You can use underscore.js
frasis = YOUROBJECT.map(function(el){return el.frasi;});
frasis = _(frasis).faltten();
Then use it to build your ListView

Categories