How to add a new property to a JSON object? - javascript

I've searched the questions here and on Google and I find answers, but they don't seem to work in my code. I want to add a new PhotoIndex property to a JSON object in AngularJS. I have the code below which works (the photos appear), but doesn't have the PhotoIndex property:
$scope.GetImagesOnBar = function ()
{
var photos = $scope.$parent.Photos;
return photos;
};
When I try to add the PhotoIndex property to the JSON array object named photos, it breaks the code and the photos no longer appear. Can you tell me why?
$scope.GetImagesOnBar = function ()
{
var photos = $scope.$parent.Photos;
var index = 0;
for (var i = $scope.ImageFirst; i <= $scope.ImageLast; i++)
{
photos[i].PhotoIndex = index;
index = index + 1;
}
return photos;
};

OK, thanks for the comments. I checked the console as suggested and saw the error "$scope.$parent.Photos[i] is undefined". I don't know why, but I decide to try angular.foreach and that worked. See the solution below.
$scope.GetImagesOnBar = function ()
{
var index = 0;
angular.forEach($scope.$parent.Photos, function (photo)
{
photo.PhotoIndex = index;
index = index + 1;
});
return $scope.$parent.Photos;
};

Related

Iterating over a nodelist, getting the length of a value of a node item

I am trying to dynamically grab ZIPcodes and validate them when the length is 5.
I used querySelectorAll to grab the Zipcode fields on the page, as well as a few other fields I will use after validating.
I iterate over the nodelist and pass it to another function, where the eventlistener kicks off if the value is the correct length.
function GetZipCodeDetails() {
var zipId = document.querySelectorAll("[id*='ZipCode']");
var countyId = document.querySelectorAll("[id*='CountyId']");
var stateId = document.querySelectorAll("[id*='StateId']");
var phoneId = document.querySelectorAll("[id*='PhoneNumber']");
for (var i = 0; i < zipId.length; i++) {
if (zipId[i].length = 5)
AssortedZipCodeFunctions(zipId[i], countyId[i], stateId[i], phoneId[i]);
}
}
function AssortedZipCodeFunctions(zipId, countyId, stateId, phoneId) {
//Runs auto-county/state function only when zipcode field is completed
document.addEventListener("keyup", (e) => {
if (zipId.value.length == 5) {
GetCountyAndStateFromIds(zipId, countyId, stateId, phoneId);
} });
}
The code works perfectly for me as it is listed above; I am just trying to move the second function into the first function, but I can't figure out how. I am just stuck on how come I can't do the following:
function GetZipCodeDetails() {
var zipId = document.querySelectorAll("[id*='ZipCode']");
var countyId = document.querySelectorAll("[id*='CountyId']");
var stateId = document.querySelectorAll("[id*='StateId']");
var phoneId = document.querySelectorAll("[id*='PhoneNumber']");
for (var i = 0; i < zipId.length; i++) {
document.addEventListener("keyup", (e) => {
if (zipId[i].value.length == 5) {
GetCountyAndStateFromIds(zipId[i], countyId[i], stateId[i], phoneId[i]);
}
});
}
}
The above gives: "TypeError: Cannot read property 'value' of undefined
at HTMLDocument."
I have figured out that the for loop is calling the second function, instead of waiting until the Zipcode value is 5... so all that happened is I passed it to another function? Or maybe I am stuck on how to get the length of the value of a node item? Please help.
In your event listener you are adding it to the document instead of each element separately
for (var i = 0; i < zipId.length; i++) {
zipId[I].addEventListener("keyup", (e) => {
if (zipId[i].value.length == 5) {
GetCountyAndStateFromIds(zipId[i], countyId[i], stateId[i], phoneId[i]);
}
});
}

Pull specific field from nth index of Knockout observableArray of objects

I have a page that displays a list of file templates built using the following method.
var loadCustomTemplate = function () {
loadBaseTemplate();
var res = 0;
for (i = 0; i < self.GetSeam().length; i++) {
var a = self.count() + 1;
self.count(a);
res = self.GetSeam()[i].FileFormat.split("_");
if (res.length == 4) {
var ap = res[3].split('.');
self.append(ap[0]);
} else {
self.append("");
}
var obj = {
Code: ko.observable(self.code()),
Number: ko.observable(self.number()),
SeamReportPath: ko.observable(self.reportPath()),
FileFormat: ko.observable(self.append()),
SequenceNumber: ko.observable(a)
}
self.CustomTemplate.push(obj);
}
self.count(0);
};
The user is then allowed to edit the fields as needed. They can also add records or remove them as needed. The method to add a record is as follows.
self.addTemplate = function () {
var count = self.CustomTemplate().length + 1;
var obj = {
Code: ko.observable(self.code()),
Number: ko.observable(self.number()),
SeamReportPath: ko.observable(self.reportPath()),
FileFormat: ko.observable(""),
SequenceNumber: ko.observable(count)
}
self.CustomTemplate.push(obj)
};
Once those updates are made they can save the updated CustomTemplate. This uses ajax that is not important to this question. The save method calls a validation method that is supposed to check to make sure there are no duplicate FileFormat fields in the object array. This is what I have, but it is failing.
var validateTemplates = function() {
for (i = 0; i < self.CustomTemplate().length; i++) {
var checkVal = self.CustomTemplate()[i].FileFormat;
var checkSeq = self.CustomTemplate()[i].SequenceNumber;
for (j = 0; j < self.CustomTemplate().length; j++) {
if (checkSeq !== self.CustomTemplate()[j].SequenceNumber ){
if (checkVal+"" === self.CustomTemplate()[j].FileFormat) {
if (checkSeq == self.CustomTemplate()[j].SequenceNumber ){
return false;
}
}
}
}
return true;
};
The problem is that when checking self.CustomTemplate()[i].FileFormat and self.CustomTemplate()[i].SequenceNumber it isn't reflecting the data displaying on the page or the data being sent to the controller (MVC 4). If I put either of those in an alert it is showing a function. How do I access the data in those specific fields for comparison?
Thanks in advance.
If I put either of those in an alert it is showing a function.
That's because you're doing this kind of thing:
var checkVal = self.CustomTemplate()[i].FileFormat;
FileFormat is the result of ko.observable(...), which returns a function, so checkVal in fact does contain a function.
The solution is for all those cases to do this:
var checkVal = self.CustomTemplate()[i].FileFormat(); // Parentheses at the end!
The parentheses execute the observable function, and if you do so without parameters you "get" the value of that observable. (If you would pass in a value, it would "set" the observable to that value.)

Making a generic code of Angular JS by using Arrays

I'm trying to make a generic code to make it in a simple way for next level purpose. Please find the commented code which I've made. But, its not working.
var app = angular.module('myApp', []);
app.factory('mainFactory',function($http){
return {
getData: function() {
return $http.get("data.json");
}
};
});
app.controller('mainCtrl', function($scope,$http,mainFactory){
var data = mainFactory.getData();
if(angular.isDefined(data)) {
data.success(function(d,s){
// I want this commented out code for the four lines defined below.
/*var a = [{name:"imagesArray"},{name:"taskArray"},{name:"courseArray"},{name:"newsArray"}];
for(var i = 0; i < a.length; i++) {
$scope.a[i].name = d.a[i].name ? d.a[i].name : [];
}*/
// I dont want this number of lines.
$scope.imagesArray = d.imagesArray ? d.imagesArray : [];
$scope.taskArray = d.taskArray ? d.taskArray : [];
$scope.courseArray = d.courseArray ? d.courseArray : [];
$scope.newsArray = d.newsArray ? d.newsArray : [];
});
}
});
If it can be simplified further, please let me know
You can use this for better way to define scope property, edit your code like below:
data.success(function(d,s){
var keyCollection = ["imagesArray","taskArray","courseArray","newsArray"];
keyCollection.forEach(function(key){
$scope[key] = d[key];
});
});
or if your project are also using lodash(or underscore).You can simply do this(just further , don't import lodash or underscore just because this one, no need cost):
// outside, create advance function
var advancePick = _.partialRight(_.pick, "imagesArray", "taskArray", "courseArray", "newsArray");
data.success(function(d,s){
// just beauty like this
_.extend($scope, advancePick(d));
});
Change $scope.a[i].name = d.a[i].name ? d.a[i].name : []; to
$scope[a[i].name] = d[a[i].name] || [];
In Javascript, you can access properties two ways :
1)
item.property
2)
item["property"]
Thomas already gave you the right answer.
If you want to simplify it further, you can get rid of the definition of your "var a" array by making an object. Then you can enumerate each public property using :
for(var propertyName in yourObject) {
// propertyName is is the name of each property
// you can get the value using: yourObject[propertyName ]
}
Happy coding!
var a = ["imagesArray","taskArray","courseArray","newsArray"];
for(var i = 0; i < a.length; i++) {
$scope[a[i]] = d[a[i]] ? d[a[i]] : [];
}
with the name version
var a = [{name:"imagesArray"},{name:"taskArray"},{name:"courseArray"},{name:"newsArray"}];
for(var i = 0; i < a.length; i++) {
$scope[a[i].name] = d[a[i].name] ? d[a[i].name] : [];
}
A last solution and the shortest most of all!
angular.merge($scope, d);
EDIT : this will work only if d is properly initialized before.
Other simplifications not related to those 4 lines:
you don't need $http injected into your controller, and you don't
need to test the result of the call to getData(): it returns a
promise, take that as given.
Also use .then(...) rather than the deprecated .success(...).
You
might however want to add .catch(...) onto the promise to handle or
at least log errors, but if just to log errors you can put that inside .getData() and keep the controller short.
Here's how those changes might look:
var app = angular.module('myApp', []);
app.factory('mainFactory',function($http, $log){
return {
getData: function() {
return $http.get("data.json")
.catch(function (e) { $log.error(e); });
}
};
});
app.controller('mainCtrl', function($scope,mainFactory){
mainFactory.getData()
.then(function(d){
// choose one of the other answers for the code here...
var keyCollection = ["imagesArray","taskArray","courseArray","newsArray"];
keyCollection.forEach(function(key){
$scope[key] = d[key];
});
});
});

Javascript object functions not working

I'm not really sure why my code isn't running correctly.. what I'm trying to do is create a grocery list object that has a couple of functions to add and remove items..
I can instantiate the objects with new items but my functions don't seem to work for some reason.
If you could save me the few hairs left in my head and tell me where the issue is I would greatly appreciate it.
var groceryList = function(itemNames,quantity) {
if (Array.isArray(itemNames)) {
this.items = itemNames;
this.quantity = quantity
this.addItems = function(newItems){
if ( Array.isArray(newItems) ) {
this.items.concat(newItems);
} else {
console.log("Please enter the items in an array fashion!");
};
};
this.removeItem = function(name) {
var listSize = this.items.length;
for (var i = 0; i < listSize; i++) {
if (this.items[i] == name) {
this.items.splice(i,1);
break;
} else {
console.log("Please enter the items in an array fashion!")
};
};
};
} else {
console.log("Please enter the items in an array fashion!")
};
};
.concat() returns a new array so you have to assign the result back to your instance variable.
So this:
this.items.concat(newItems);
needs to be changed to this:
this.items = this.items.concat(newItems);
or, you could actually use this to append to the array directly:
this.items.push.apply(this.items, newItems);
Because .push() can take more than one argument.
Then, in your .removeItem() function, you need to remove the item you actually found by changing this:
this.items.splice(2,1);
to this:
this.items.splice(i,1);

With KnockoutJS, how to bind to a child property of an array item

I have a question similar to Bind text to property of child object and I am having difficulty properly creating the KO observable for a child object.
For example, I do an HTTP Get to return a JSON array of People, and the People array is inside a property called "payload". I can get the basic binding to work, and do a foreach on the payload property, displaying properties of each Person; however, what I need to do is add a "status" property to each Person, which is received from a different JSON, example
/api/people (firstname, lastname, DOB, etc.)
/api/people/1/status (bp, weight, height, etc)
I have tried binding to status.bp, and status().bp, but no luck.
The js example:
var TestModel = function (data) {
var len = data.payload.length;
for (var i = 0; i < len; i++) {
var elem = data.payload[i];
var statusdata = $.getJSON("http://localhost:1234/api/people/" + elem.id + "/status.json", function (statusdata) {
elem.status = statusdata;
data.payload[i] = elem;
});
}
ko.mapping.fromJS(data, {}, this);
};
var people;
var data = $.getJSON("http://localhost:1234/api/people.json", function (data) {
people= new TestModel(data);
ko.applyBindings(people);
});
2 important things I will need:
1) properly notify KO that "payload" is an array to key on ID property
2) make "status" an observable
Help!
[UPDATE] EDIT with working fix based on Dan's answer:
var TestModel = function(data) {
...
this.refresh = function () {
$.getJSON("http://localhost:1234/api/people", function (data) {
self.payload = ko.observableArray(); // this was the trick that did it.
var len = data.payload.length;
for (var i = 0; i < len; i++) {
var elem = data.payload[i];
$.getJSON("http://localhost:1234/api/people/" + elem.id + "/status", function (statusdata) {
// statusdata is a complex object
elem.status = ko.mapping.fromJS(statusdata);
self.payload.push(elem);
});
}
// apply the binding only once, because Refresh will be called with SetInterval
if (applyBinding) {
applyBinding = false;
ko.applyBindings(self);
}
}
I am still new to Knockout and improvements to the refresh function are most welcome. The mapping is still being reapplied each time.
You need to define an observable array and then push your data into it.
elem.status = ko.observableArray();
for (var i = 0; i < statusdata.length; i++) {
elem.status.push(statusdata[i]);
}
I can't tell what the full structure of the data is by the example. But if status is a complex object, you may what to give it its own model.
for (var i = 0; i < statusdata.length; i++) {
elem.status.push(new statusModel(statusdata[i]));
}

Categories