I would like to understand a certain behavior of AngularJS when it comes to watching changes of objects provided by a factory.
There is a different behavior depending on whether I read a property of a factory-provided object in the controller or in the html.
Compare the following 2 ways of displaying the property in the view:
app.controller("testController", function($scope, testFactory){
$scope.test_obj = testFactory.read();
$scope.test_prop = testFactory.read().prop;
});
<div>{{test_obj.prop}}</div>
<div>{{test_prop}}</div>
When the property changes in the testFactory, the change is updated in the view only in the first case, when the whole object is declared to scope and the property is called in the view. When the property is directly declared to the scope, it doesn't update automatically in the view.
This behavior can be observed in the following jsfiddle: https://jsfiddle.net/fb86p4fm/
What's the reason for this behavior?
$scope.test_prop = testFactory.read().prop;
In the above line, the initial value of prop is 0. In javascript, numbers are copied by value, but objects/arrays are by reference. $scope.test_obj is referencing the service object, while $scope.test_prop is not.
I would say that $scope.test_obj is a reference to the factory's obj while $scope.test_prop is a copied value of obj.prop.
If you add the following to your example :
console.log(testFactory.read()); // output : Object {prop: 0}
console.log(testFactory.read().prop); // output 0
You'd see that the read method would return an object and the read().prop would return a value.
Related
I'm not sure if the title is correctly conveying the feature i'd like clarity on. But let me lay it out.
In javascript we can instantiate a variable to an empty object:
var person = {};
We can easily set properties to it
person.name = "Bob"
We can't, however, assign objects by using properties:
person.pet.name = "Bob"
> Uncaught TypeError: Cannot set property 'name' of undefined
In angular you can. This modest code for example. I have a controller:
angular.module('someModule', [])
.controller('someController', [
Callback
]
function Callback(){
this.person = {}
}
Then the view contains:
<input type="text" data-ng-model='vm.person.pet.name'>
Where vm is the viewmodel representing the controller. When I log the results grabbed from the input you get a person object with a pet child object with a property of name
What gives? Why did angular add this feature?(i assume feature) Is it strictly for flexibility?
From the docs:
Forgiving
Expression evaluation is forgiving to undefined and null. In JavaScript, evaluating a.b.c throws an exception if a is not an object. While this makes sense for a general purpose language, the expression evaluations are primarily used for data binding, which often look like this:
{{a.b.c}}
It makes more sense to show nothing than to throw an exception if a is undefined (perhaps we are waiting for the server response, and it will become defined soon). If expression evaluation wasn't forgiving we'd have to write bindings that clutter the code, for example: {{((a||{}).b||{}).c}}
Similarly, invoking a function a.b.c() on undefined or null simply returns undefined.
In short, expression in html <input type="text" data-ng-model='vm.person.pet.name'> doesn't change in time, however an actual value of vm.person.pet.name does.
I think this is a feature. Imagine that you have an array of objects of different types e.g. people and animal.
people object:
{
name: "someName",
lastname: "someLastName
}
and
animal object:
{
name: "someName"
//Without lastname
}
If you put an array of objects, which contains these 2 types of objects, to the ng-repeat you could easily avoid code crash if you try to access some property which is not part of the every object.
I'm have having some trouble accessing a value from the parent scope, and what appears to be some strange behaviour also.
If I log $scope.$parent to the console and inspect the DOM object there is a property topicDiscovery.name. However, when I try to log this ($scope.$parent.topicDiscovery.name) it returns undefined.
Also, when I try to log the topicDiscovery object it returns an empty array, even though when its not empty in the DOM for $scope.$parent.
Why is this?
This means $scope.$parent.topicDiscovery is getting changed after console.log. Google chrome doesnot print all json object when you do console.log. It just refer to current Object. Try using JSON.stringify($scope.$parent), Here you will not get this property as stringify converts that JS object to string and there will not be any memory linking between orignal object and string.
Here is best example.
var d={z:{b:{}}};
console.log(d); //When you check here, d.a_1 is available
console.log("d.a_1",d.a); //Here d.a_1 is not available.
//Here I am adding d.a_1 property
for(var i=0;i<10;i++){
d['a_'+i]=i;
}
I have a Javascript object that gets initialised in an asynchronous manner, later edited and then stored in an array until it can be saved. The problem is that one of the checks it goes through is _.has(obj, 'Id'). This test always fails. I've used JSON.stringify() to log the object's internals and everything has its value and looks correct. When I run a simple test of this line with hard-coded values in plunker, everything works as expected. I've tried accessing the Id values through obj.Id and obj['Id'] but these both return undefined and I don't know why. Why can stringify read the property but a regular accessor can't? The Id is a value that is not touched between initialisation and saving of the object.
I have learned that objects in javascript is passed by reference, so if I delete one, they will both be inaccessible.
Now,
var self = self.parent.modules[moduleId].slideshow;
delete self.parent.modules[moduleId].slideshow; //remove the module object from the JSON
console.error('deleted self. it is now:');
console.error(self.parent.modules[moduleId].slideshow);
that console prints undefined, as expected. However, if I do this:
console.error('deleted self. it is now:');
console.error(self);
It still has the object to present me with, as if it was actually cloned?
You deleted the property not the value that property referenced.
Other references that value are unaffected.
I'm working on an AngularJS app. When I console.log an object (the attrs parameter of directive linking function) the browser show unconsistent results for the parameter "editable" (see image). In Chrome, the property gets valued as both "zzz" and undefined (see 5th row vs 1st). In Safari the output is displayed differently, but on console.log(object) the "editable" property appears as "zzz", while on console.log(object.editable) the property is undefined.
Any hints ?
I think this issue is related to: console.log() showing contradictory values for the same object property
I'll guess that your HTML is something like this
<div my-directive editable="{{someScopeProperty}}"...></div>
and that you are calling console.log() in your link function. When the link function runs, interpolated attributes are not defined yet (you need to use $observe or $watch to asynchronously get the interpolated value), so you'll get undefined if you attempt to log the value. Soon after, the value gets defined, and Chrome seems to automatically update the value (which is really a reference, I think) in the console where you logged the full object (not just the individual value).