ko.utils.arrayMap with Associative JSON object - javascript

I am trying to loop over an associative JSON object using ko.utils.arrayMap(), but the function never enters the loop when the it is done in this manner. Can anyone suggest how I can get at these properties. Possible I'm using the wrong utility function for thisstyle of JSON?
http://jsfiddle.net/rVPBz/
var data = [
{
"job_id":"AM28200",
"inspection":{
"5":{
"inspection_type_id":5,
"inspection_type_name":"hydro",
"display_name":"Requested",
"html_name":"Hydro",
"itp":"Stage H1. H2, H3",
"1":{
"dates":[
"2013-10-21"
],
"inspectors":[
" "
]
},
"2":{
"dates":[
"2013-10-21"
],
"inspectors":[
" "
]
},
"3":{
"dates":[
"2013-10-21"
],
"inspectors":[
" "
]
},
"4":{
"dates":[
"2013-10-21"
],
"inspectors":[
" "
]
},
"5":{
"dates":[
"2013-10-21"
],
"inspectors":[
" "
]
}
}
}
},
{
"job_id":"AM28212",
"inspection":{
"5":{
"inspection_type_id":5,
"inspection_type_name":"hydro",
"display_name":"Waived",
"html_name":"Hydro",
"itp":"Stage H1. H2, H3",
"1":{
"dates":[
"2013-10-21"
],
"inspectors":[
" "
]
}
}
}
}
]
var InspectionDiary = function() {
var self = this;
self.jobs = ko.observableArray([])
function Job(data){
var self = this;
self.job_id = data.job_id;
self.types = ko.observableArray([])
}
function Type(data){
var self = this;
self.type_id = ko.observable(data.inspection_type_id);
self.name = ko.observable(data.inspection_type_name);
self.display_name = ko.observable(data.display_name);
self.html_name = ko.observable(data.html_name);
self.itp = ko.observable(data.itp);
self.inspectors = ko.observableArray([])
self.dates = ko.observableArray([]);
}
var jobs = ko.utils.arrayMap(data, function(item) {
var job = new Job(item);
var types = ko.utils.arrayMap(item.inspection, function(item) {
var type = new Type(item)
type.dates = ko.utils.arrayMap(item.dates(), function(item) {
return new Dates(item);
})
type.inspectors = ko.utils.arrayMap(type.inspectors(), function(item) {
return new Inspector(item);
})
return type;
})
job.types(types);
return job;
});
self.jobs(jobs);
}
ko.applyBindings(new InspectionDiary())

The ko.utils.arrayMap is designed to be used on an array, not for an object.
To loop through an object you can use the following function :
var objectMap = function(o, handler){
var res = {};
for(var key in o) {
res[key] = handler(o[key])
}
return res;
};
As the mapping doc says :
ko.utils.arrayMap, which executes a function for each item in an array and pushes the result of the function to a new array that is returned.
See fiddle

The problem is that the json does not contain arrays in the proper places. For example, inspection should be an array and it isn't. Since it looks like you aren't doing a lot of extra stuff in the viewModels, you could try this for an easier mapping from JSON to VMs: http://knockoutjs.com/documentation/plugins-mapping.html

Related

How to bind data to multiple elements using javascript

I need to implement one-way data binding in multiple places using a JSON object.
Below is the JSON object getting from the server every 10 sec, with the latest status of a user.
<html>
<head></head>
<body>
<ul>
<li>User0: <span class="change_status" unique_id="7150032">Busy</span></li>
<li>User1: <span class="change_status" unique_id="7150031">Offline</span></li>
<li>User2: <span class="change_status" unique_id="7150030">Online</span></li>
</ul>
<div class="profile">
<div class="profileimage">
U
</div>
Status: <span class="change_status" unique_id="7150032">Busy</span>
</div>
<script>
function Binding(b) {
_this = this
this.elementBindings = []
this.value = b.object['unique_id']
this.valueGetter = function(){
return _this.value;
}
this.valueSetter = function(val){
_this.value = val
for (var i = 0; i < _this.elementBindings.length; i++) {
var binding=_this.elementBindings[i]
binding.element[binding.attribute] = val
}
}
this.addBinding = function(element, unique_id){
var binding = {
element: element,
unique_id: unique_id
}
this.elementBindings.push(binding)
element['innerText'] = _this.value
return _this
}
Object.defineProperty(b.object, b.property, {
get: this.valueGetter,
set: this.valueSetter
});
b.object[b.property] = this.value;
}
obj = [
{
"unique_id": "7150032",
"status_name": "Busy"
},
{
"unique_id": "7150031",
"status_name": "Offline"
},
{
"unique_id": "7150030",
"status_name": "Online",
}
]
var changeStatusElements = document.getElementsByClassName("change_status")
var binding = new Binding({
object: obj
})
for(var i=0; i< changeStatusElements.length; i++){
var unique_id = changeStatusElements[i]['unique_id']
binding.addBinding(changeStatusElements[i], unique_id)
}
</script>
</body>
</html>
These statuses will change based on the new JSON that has new statuses. Please assume the JSON (contains a live status of the users) will change every 10 sec.
Please correct my code that would be appreciated.
Give your ul element an id attribute, so you can reference it from code. I will assume it is called "container". Initially it should just be an empty element, like
<ul id="container"></ul>
You should also have a URL where to make the request to. I'll assume you have that in a variable url.
Then, you can add this code:
(async function request() {
let response = await fetch(url);
let { data } = await response.json();
document.querySelector("#container").innerHTML = data.map(({ unique_id, status_name }, i) =>
`<li>User${i}: <span class="change_status" unique_id="${unique_id}">${status_name}</span></li>`).join("\n");
setTimeout(request, 10000);
})();
I have done this. Please check the following code
function Binding(b) {
_this = this
this.statusArrProxy = b[b.property]
this.statusGetter = function () {
return _this.statusArrProxy;
}
this.statusSetter = function (arr) {
_this.statusArrProxy = arr
arr.forEach(a => {
var elements = document.querySelectorAll('.change_status[' + b.key + '="' + a[b.key] + '"]')
elements.forEach(node => node[b.attribute] = a[b.value])
})
}
Object.defineProperty(b, b.property, {
get: this.statusGetter,
set: this.statusSetter
})
b[b.property] = this.statusArrProxy
}
var statusArray = [{ unique_id: 123, status: 'Busy' }, { unique_id: 1234, status: 'Available' }]
var bindingObj = {
statusArray: statusArray,
attribute: 'innerHTML',
property: 'statusArray',
key: 'unique_id',
value: 'status'
}
new Binding(bindingObj)
//replace the entire array bindingObj.statusArray = [{ unique_id: 123, status: 'Offline' }, { unique_id: 1234, status: 'Out Of Office' }]
thank you for all support

Using the Alexa-SDK how can I get this.emit() method to return multiple objects in a JSON array?

I'm trying to get multiple account id's from a JSON array using the emit() method. The emit method is defined from the Alexa-SDK. The problem is that its emitting only one of the values and not all. How can I get the emit()method to return both object values in a JSON array?
Here is my code snippet:
'use strict';
const Alexa = require('alexa-sdk');
const handlers = {
'LaunchRequest': function() {
this.emit(':tell', 'sure');
this.emit('getEmployeeInfoIntent');
},
'getEmployeeInfoIntent': function() {
var empinfoData = {
"employees": [{
"account_id": 8675309
},{
"account_id": 54321
}]
};
for (var i in empinfoData) {
var employeeInfo = empinfoData[i].account_id;
this.emit(':tell', 'The accounts available are id number' + employeeInfo);
//return only 8675309 but I want 8675309 and 54321
};
}
}; ///end of handler
exports.handler = (event, context) => {
const alexa = Alexa.handler(event, context);
alexa.APP_ID = APP_ID;
alexa.registerHandlers(handlers);
alexa.execute();
};
var objtwo = {
"employees": [{
"account_id": 8675309
},{
"account_id": 54321
}]
}
// mythis variable is defined globally
var mythis = this;
function getJSONArray() {
//var reqresponse = this.responseText;
//var objtwo = JSON.parse(reqresponse);
var empinfoData = objtwo.employees;
var allIds = [];
for (var i of empinfoData) {
var employeeInfo = i["account_id"];
allIds.push(employeeInfo);
};
return allIds;
};
console.log(getJSONArray());
Save the emploeeInfo's into an Array and emit the array after the loop.

extract fields in javascript

I have a dictionary where i want to extract some fields with their keys. So what i want is call a function where i pass this dictionary and the keys.
var posts = {
"code":"ok",
"data":[
{
"id":1,
"username":"example1",
"data":
{
"id":4,
"name":"fran"
},
},
{
"id":2,
"username":"example2",
"data":
{
"id":5,
"name":"manuel"
}
}
]
};
So I would like to have a new dictionary where i have the nested value as a simple dictionary value.
[{
"id":1,
"username":"example1",
"name":"fran"
},
"id":2,
"username":"example2",
"name":"manuel"
}}
function dict(obj)
{
var list = Object.assign({},obj).data;
list.forEach((e,i,a) => {
e.name = e.data.name;
delete e.data;
});
return list;
}
that's how you dictionary function should look
I have tried this one. I think i have problem because i am creating a dictionary when node is "name" in the iteration. So i want to confirm if it's OK or there is any another way to do this.
var list = [];
var dict = {};
function iterate(obj, stack) {
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object") {
iterate(obj[property], stack + '.' + property);
} else {
console.log(property + " " + obj[property]);
dict[property] = obj[property];
if(property=="name"){
list.push(dict);
dict ={};
}
}
}
}
return list;
}
var simplified = iterate(posts.data, '');
console.log(simplified);

Access methods of js module with Jasmine so they can be individually tested

I'm doing some unit testing with Jasmine. I have a module getContacts which is successfully injected to the spec using module.exports and require. Though I have access to the module itself, the modules methods are returning undefined so I am not able to test them as units. Below is the code for the module:
var getContacts = function() {
var GetContacts = this;
var contacts = require('nativescript-contacts');
var model = require("../main-view-model");
GetContacts.init = (function() {
var self = this;
contacts.getContact().then(function(args){
self.makeName(args.data);
self.getPhoneNumber(args.data);
}).catch(function(e) {
console.log("promise \"contacts.getContact()\" failed with" + e.stack + "\n" + "value of self:" + " " + self)
});
}());
GetContacts.getPhoneNumber = function(data) {
if(data.phoneNumbers.length > 0){
model.phone = data.phoneNumbers[0];
}
};
GetContacts.makeName = function(data) {
if(data.name.displayname) {
model.contactName = data.name.displayname;
}
else {
}
model.contactName = data.name.given + " " + data.name.family;
};
};
module.exports = getContacts;
and the spec file:
describe("getContacts", function() {
"use strict";
var contacts, model, getContacts, data;
beforeEach(function() {
contacts = require('nativescript-contacts');
model = require("../main-view-model");
getContacts = require('../src/getContacts.js');
data = {
"data": {
"name": {
"given": "John",
"middle": "Peter",
"family": "Smith",
"prefix": "Mr.",
"suffix": "Jr.",
"testEmptyObject": {},
"testEmptyString": "",
"testNumber": 0,
"testNull": null,
"testBool": true,
"displayname": "John Smith",
"phonetic": {
"given": null,
"middle": null,
"family": null
}
}
},
"response": "selected"
}
});
it("Gets the display name as contact name if display name is a string with length", function() {
expect(getContacts.makeName(data)).toBe("John Smith");
});
});
The test fails with the error:
getContacts.makeName is not a function
and indeed logging it returns undefined. Logging getContacts however prints the entire getContacts function to the console. How to I access makeName and other methods so I can unit test them at an individual level?
The code needed to be restructured without the wrapping anonymous function. I suppose it isn't needed since the module itself creates separation. Here is the code that worked:
var contacts = require('nativescript-contacts');
var model = require("../main-view-model");
var GetContacts = {
init: function() {
var self = this;
contacts.getContact().then(function(args){
model.contactName = self.makeName(args.data);
model.phone = self.getPhoneNumber(args.data);
}).catch(function(e) {
console.log("promise \"contacts.getContact()\" failed with" + e.stack + "\n" + "value of self:" + " " + self)
});
},
getPhoneNumber: function(data) {
if(data.phoneNumbers.length > 0){
return data.phoneNumbers[0];
}
},
makeName: function(data) {
if(data.name.displayname) {
return data.name.displayname;
}
else {
}
}
};
module.exports = GetContacts;
function don't return anything so function will do something but won't know further reference to code inside of it, so try add return at end of function like below ;)
var getContacts = function() {
var GetContacts = this;
/*
rest of code
*/
GetContacts.makeName = function(data) {
if(data.name.displayname) {
model.contactName = data.name.displayname;
}
else {
}
model.contactName = data.name.given + " " + data.name.family;
};
//add return to export content of function
return GetContacts;
};

Access object context from prototype functions JavaScript

I have problems with object scope.
Here is my class code
// Table list module
function DynamicItemList(data, settings, fields) {
if (!(this instanceof DynamicItemList)) {
return new DynamicItemList(data, settings, fields);
}
this.data = data;
this.settings = settings;
this.fields = fields;
this.dataSet = {
"Result": "OK",
"Records": this.data ? JSON.parse(this.data) : []
};
this.items = this.dataSet["Records"];
this.generateId = makeIdCounter(findMaxInArray(this.dataSet["Records"], "id") + 1);
this.dataHiddenInput = $(this.settings["hidden-input"]);
}
DynamicItemList.RESULT_OK = {"Result": "OK"};
DynamicItemList.RESULT_ERROR = {"Result": "Error", "Message": "Error occurred"};
DynamicItemList.prototype = (function () {
var _self = this;
var fetchItemsList = function (postData, jtParams) {
return _self.dataSet;
};
var createItem = function (item) {
item = parseQueryString(item);
item.id = this.generateId();
_self.items.push(item);
return {
"Result": "OK",
"Record": item
}
};
var removeItem = function (postData) {
_self.items = removeFromArrayByPropertyValue(_self.items, "id", postData.id);
_self.dataSet["Records"] = _self.items;
_self.generateId = makeIdCounter(findMaxInArray(_self.dataSet["Records"], "id") + 1);
return DynamicItemList.RESULT_OK;
};
return {
setupTable: function () {
$(_self.settings["table-container"]).jtable({
title: _self.settings['title'],
actions: {
listAction: fetchItemsList,
deleteAction: removeItem
},
fields: _self.fields
});
},
load: function () {
$(_self.settings['table-container']).jtable('load');
},
submit: function () {
_self.dataHiddenInput.val(JSON.stringify(_self.dataSet["Records"]));
}
};
})();
I have problems with accessing object fields.
I tried to use self to maintain calling scope. But because it is initialized firstly from global scope, I get Window object saved in _self.
Without _self just with this it also doesn't work . Because as I can guess my functions fetchItemsList are called from the jTable context and than this points to Window object, so I get error undefined.
I have tried different ways, but none of them work.
Please suggest how can I solve this problem.
Thx.
UPDATE
Here is version with all method being exposed as public.
// Table list module
function DynamicItemList(data, settings, fields) {
if (!(this instanceof DynamicItemList)) {
return new DynamicItemList(data, settings, fields);
}
this.data = data;
this.settings = settings;
this.fields = fields;
this.dataSet = {
"Result": "OK",
"Records": this.data ? JSON.parse(this.data) : []
};
this.items = this.dataSet["Records"];
this.generateId = makeIdCounter(findMaxInArray(this.dataSet["Records"], "id") + 1);
this.dataHiddenInput = $(this.settings["hidden-input"]);
}
DynamicItemList.RESULT_OK = {"Result": "OK"};
DynamicItemList.RESULT_ERROR = {"Result": "Error", "Message": "Error occurred"};
DynamicItemList.prototype.fetchItemsList = function (postData, jtParams) {
return this.dataSet;
};
DynamicItemList.prototype.createItem = function (item) {
item = parseQueryString(item);
item.id = this.generateId();
this.items.push(item);
return {
"Result": "OK",
"Record": item
}
};
DynamicItemList.prototype.setupTable = function () {
$(this.settings["table-container"]).jtable({
title: this.settings['title'],
actions: this,
fields: this.fields
});
};
DynamicItemList.prototype.load = function () {
$(this.settings['table-container']).jtable('load');
};
DynamicItemList.prototype.submit = function () {
this.dataHiddenInput.val(JSON.stringify(this.dataSet["Records"]));
};
DynamicItemList.prototype.removeItem = function (postData) {
this.items = removeFromArrayByPropertyValue(this.items, "id", postData.id);
this.dataSet["Records"] = this.items;
this.generateId = makeIdCounter(findMaxInArray(this.dataSet["Records"], "id") + 1);
return DynamicItemList.RESULT_OK;
};
DynamicItemList.prototype.updateItem = function (postData) {
postData = parseQueryString(postData);
var indexObjToUpdate = findIndexOfObjByPropertyValue(this.items, "id", postData.id);
if (indexObjToUpdate >= 0) {
this.items[indexObjToUpdate] = postData;
return DynamicItemList.RESULT_OK;
}
else {
return DynamicItemList.RESULT_ERROR;
}
};
Your assigning a function directly to the prototype. DynamicItemList.prototype= Normally it's the form DynamicItemList.prototype.somefunc=
Thanks everyone for help, I've just figured out where is the problem.
As for last version with methods exposed as public.
Problematic part is
$(this.settings["table-container"]).jtable({
title: this.settings['title'],
actions: {
listAction: this.fetchItemsList,
createAction: this.createItem,
updateAction: this.updateItem,
deleteAction: this.removeItem
},
fields: this.fields
});
};
Here new object is created which has no idea about variable of object where it is being created.
I've I changed my code to the following as you can see above.
$(this.settings["table-container"]).jtable({
title: this.settings['title'],
actions: this,
fields: this.fields
});
And now it works like a charm. If this method has drawbacks, please let me know.
My problem was initially in this part and keeping methods private doesn't make any sense because my object is used by another library.
Thx everyone.
You need to make your prototype methods use the this keyword (so that they dyynamically receive the instance they were called upon), but you need to bind the instance in the callbacks that you pass into jtable.
DynamicItemList.prototype.setupTable = function () {
var self = this;
function fetchItemsList(postData, jtParams) {
return self.dataSet;
}
function createItem(item) {
item = parseQueryString(item);
item.id = self.generateId();
self.items.push(item);
return {
"Result": "OK",
"Record": item
};
}
… // other callbacks
$(this.settings["table-container"]).jtable({
title: this.settings['title'],
actions: {
listAction: fetchItemsList,
createAction: createItem,
updateAction: updateItem,
deleteAction: removeItem
},
fields: this.fields
});
};

Categories

Resources