I am running an Angular app that pulls its data from a Web API service. The API returns the objects as JSON and the Angular service (via $http.get() ) returns them to the controller as an array of objects. Pretty normal stuff.
What I'd like to do is add a property to each of the returned objects called "Selected". This property is purely for GUI purposes (is the object selected or not in the UI) and doesn't need to be persisted in any way. I figured the easiest thing to do was loop through the returned array of objects and just add it. So my code looks like this:
function getData() {
myService.getData()
.then(function(data) {
$scope.myData = data.results;
// Add a "Selected" property to each object
$.each($scope.myData, function(item) {
item.Selected = false;
});
}
When it gets to the line that says, "item.Selected = false" it throw an error message, saying "Cannot assign to read-only property Selected".
It is unclear to me why "Selected" is read-only? I didn't know if maybe Angular does some funky object processing when it reads data? What am I doing wrong here? Or should I be approaching this a completely different way?
Note (I'd like to avoid having to make Selected a part of the original object, as it's not representative of anything related to that object).
to add property to an object use underscorejs,
"each _.each(list, iteratee, [context]) Alias: forEach
Iterates over a list of elements, yielding each in turn to an iteratee function. The iteratee is bound to the context object, if one is passed. Each invocation of iteratee is called with three arguments: (element, index, list). If list is a JavaScript object, iteratee's arguments will be (value, key, list). Returns the list for chaining."
"extend _.extend(destination, *sources)
Copy all of the properties in the source objects over to the destination object, and return the destination object. It's in-order, so the last source will override properties of the same name in previous arguments."
$scope.myData = data.results;
// Add a "Selected" property to each object
_.each($scope.myData, function(ent) {
_.extend(ent, {
Selected : false
});
});
Your debugger screenshot actually gives you a more useful error message than what you posted (emphasis mine):
Cannot assign to read only property 'Selected' of 0
This shows that instead of the object, you're getting a number as your item variable (0 in this case). Assigning properties to primitives in strict mode throws this "Cannot assign to read-only property" error. (Without strict mode, it just fails silently.)
As JcT pointed out in a comment, this is because $.each calls the function with 2 params passed, the index first, the value second. See the documentation of $.each:
callback
Type: Function( Integer indexInArray, Object value )
So even though you named your parameter item, it received the value of the current index instead. This means your code can be fixed by just adding this missing first parameter to your callback, like this:
function getData() {
myService.getData()
.then(function(data) {
$scope.myData = data.results;
// Add a "Selected" property to each object
$.each($scope.myData, function(index, item) { //index was added here!
item.Selected = false;
});
}
Related
I'm lost on how to get object properties in JS. I'm getting values from firebase and I wanted to filter the result by its id:
//id is from query params (selecting 1 item from view)
const snippet = snippets.filter(snips => {
if(snips.id == id) return snips;
})
If I console.log after these lines, I'm getting this:
const obj = snippet[0];
So I tried to get properties by using snippet[0] which returns this:
But if I try to get properties such as:
console.log(obj['id']);
//console.log(obj.title); - tried this as well
it returns:
Entering data:
This isn't how the array::filter function works. It iterates over the array and the callback returns a boolean true/false if the element should be returned in the result array.
const snippet = snippets.filter(snips => snips.id == id)
Issue
Cannot read property "title" of undefined
This is saying that snippet[0] is currently undefined when trying to access any properties
snippet[0].title, a root title property also doesn't exist in your other console log
Solution
Your snippet is (possibly) an array of 1 (or 0) element, so in order to access the title nested in data property
snippet[0].data.title
And in the case that the array is empty or has an element without a data property, use optional chaining or guard clauses to check the access
snippet[0]?.data?.title
or
snippet[0] && snippet[0].data && snippet[0].data.title
looking at what you are asking you need to enter first the data.
console.log(obj[0].data.content)
I have an ngRepeat on a Map that gives me a "(key, value)" pair for each iteration. I now need to write a filter on this to limit some of the results I get back. I defined my filter to have two parameters. I pass one parameter in the ngRepeat call, so I should expect to get two parameters. The parameter I manually pass is a boolean. In the debugger both of those parameters were set, and the second parameter was "true", which is what I was passing, so I would expect the first parameter to correspond to my "(key, value)" pair. What I got for the first parameter was an essentially empty Object.
The reference in my HTML looks like this:
<div ng-repeat="(name, thing) in thingMap | limitThings:showOnlyFailed">
The "thingMap" is a map keyed by a "name" property, whose values are "thing" objects.
Here's my filter definition, representing what I expected to see in that first parameter:
thingModule.filter("limitThings", ['ThingsService', function(ThingsService) {
return function(entry, showOnlyFailed) {
return !showOnlyFailed || ThingsService.anyFailuresInList(entry.thing);
};
}]);
When I get into the debugger, the value of "entry" is just "Object {}".
You see thingMap as argument named entry. If you got an empty object, then you thing map is empty.
I've created a small plunk http://plnkr.co/edit/miipnB?p=preview where you can see that hash map of your things are passed corectly.
I think what just-boris is saying is correct, but I modified his example to make it a little clearer. When you use ng-repeat with an object, then the whole object is passed into the filter, not each entry.
I did some filtering with angular.forEach based on a 'failed' property in the value of the object.
http://plnkr.co/edit/B2WOiSm2vZiQBKS1wlmJ?p=preview
app.filter('limitThings', function() {
return function(entry, showOnlyFailed) {
console.log("Entry: " + entry + " Only Failed: " + showOnlyFailed);
var filteredMap = {};
angular.forEach(entry, function(value, key)
{
if(!showOnlyFailed || !value.failed)
{
filteredMap[key] = value;
}
});
return filteredMap;
}
});
I was implementing an array for my ember data property
DS.JSONTransforms.array = {
serialize: function(value) {
return Em.isNone(value) ? [] : value ;
},
deserialize: function(value) {
return Em.isNone(value) ? [] : value ;
}
};
And I created this jsbin for test to add and remove items to the array http://jsbin.com/avENazE/4/edit
If I check the console
model.get('pages').push('hi');
console.log(model.get('pages'));
I can see that the new items are corectly add to the array, but are not displayed on the view.
Also the count property is not updated and this error shows on the console on save the model
Uncaught TypeError: You must pass a resolver function as the sole argument to the promise constructor
The make the view be aware of changes of the representing model data you need data binding to work properly. To get data binding to work properly you need to use the correct functions that are sensible to bindings, so in the case of operations done to an array you can't just use vanilla push but instead pushObject or the counterpart removeObject, the same applies for setting a new value to a property, while dot notation will work it will not update you bindings therefore .set() and .get() need to be used etc.
So that said, here your working jsbin.
Hope it helps.
I have an object that contains an array of objects from which I need to get a value of their properties.
As an example this is what I need to get:
Stronghold.bins.models[0].attributes.entity.title
Which returns "Stronghold Title 1"
function grabItemName(){
var itemName=$(Stronghold.bins).each(function(){
return this.models[0].attributes.entity.title == title;
console.log(itemName);
})
};
(if there is a better way for me to ask this question please let me know)
I apologize if this was poorly asked!
The current issue is that it does not understand the array value '[0]' and cannot read it as it is undefined. What do I need to do to grab the 'title' value of all items in the array?
What do I need to do to grab the 'title' value of all items in the array?
That's what .map [docs] is for. It lets you map each value in an array to another value.
In the following I assume you want to iterate over each Stronghold.bins.models, because iterating over Stronghold.bins does not make sense with the provided information:
var titles = $.map(Stronghold.bins.models, function(obj) {
return obj.attributes.entity.title;
});
// `titles` is now an array containing `.attributes.entity.title;` of
// each object.
The current issue is that it does not understand the array value '[0]' and cannot read it as it is undefined.
Well, that won't happend anymore ;) In your example you where iterating over the properties of the Stronghold.bins object. One of these properties is models itself (!) and I doubt that any other property value has a models property.
Try using the other version of the each function:
$.each(Stronghold.bins, function () {
});
The version you are using is for looping through an element on the page, e.g $('body div p').each(function() {}), which isn't what you want in this instance: You want to loop over the values contained in Stronghold.bins.
I have an object that contains circular references, and I would like to look at the JSON representation of it. For example, if I build this object:
var myObject = {member:{}};
myObject.member.child = {};
myObject.member.child.parent = myObject.member;
and try to call
JSON.stringify(myObject);
I get the error "too much recursion", not surprisingly. The "child" object has a reference to its "parent" and the parent has a reference to its child. The JSON representation does not have to be perfectly accurate, as I'm only using it for debugging, not for sending data to a server or serializing the object into a file or anything like that. Is there a way to tell JSON.stringify to just ignore certain properties (in this case the parent property of the child object), so that I would get:
{
"member" : {
"child" : {}
}
}
The closest I can think of is to use getChild() and getParent() methods instead of just members, because JSON.stringify ignores any properties that are functions, but I'd rather not do that if I don't have to.
You can pass a function as the second argument to stringify.
This function receives as arguments the key and value of the member to stringify.
If this function returns undefined, the member will be ignored.
alert(JSON.stringify(myObject, function(k, v) {
return (k === 'member') ? undefined : v;
}));
...or use e.g. firebug or use the toSource()-method, if you only want to see whats inside the object.
alert(myObject.toSource());
From the crockford implementation (which follows the ECMA specification):
If the stringify method sees an object that contains a toJSON method, it calls that method, and stringifies the value returned. This allows an object to determine its own JSON representation.
Then something like this should work just fine:
var myObject =
{
member: { child: {} }
}
myObject.member.child.parent = myObject.member;
myObject.member.child.toJSON = function ()
{
return 'no more recursion for you.';
};
console.log(JSON.stringify(myObject));
http://jsfiddle.net/feUtk/