Accessing parent scope value with standard method - javascript

Today I wondered after some strange behavior of angularjs. I used console.log to log $scope, there were no key attached to scope named val1, but when I use console.log($scope.val1) its returning value as a object. After digging out the reason, I found that it was accessing parent scope as there is no kay named val1 in $scope. Now my question is that,
Which is good practice? Can you please justify?
Using $scope.$parent.val1
Using $scope.val1

You should generally never use $scope.$parent and instead rely on Javascripts prototypal inheritance. By accessing the parent directly, you run the risk of the code breaking if you move the directive/controller a step up in the scope hierarchy, and the data is now on $scope.$parent.$parent instead.
Instead, never write to properties directly on the scope but to objects on the scope object instead.
Say you do:
$scope.$parent.value = 'x';
Then you do:
console.log($scope.value);
That'll print x to the console. If you then do:
$scope.value = 'y';
You're not modifying the value property on the parent scope, but introducing a new property on the child scope. So $scope.$parent.value will still contain x.
To get around that, you do:
$scope.$parent.data = {value: 'x'};
console.log($scope.data.value); // prints x
$scope.data.value = 'y';
console.log($scope.data.value); // prints y
console.log($scope.$parent.data.value); // also prints y
The reason this works is that instead of creating a new value property on the child scope, it first needs to lookup the data property. And that's not on the child scope, so it goes up the prototype chain ($parent) to find the data object. The data object is found on the parent, and the value property is set on that object instead.

It depends on the type of val1, in case it is a primitive, you will have to access it explicitly via $scope.$parent.
In case it is an object, you can take advantage of the prototypal inheritance that exists between parent and child scope in Angular and just reference it regularly, as I am sure you are aware of, objects are passed by reference, so any change to the object will change it on the parent scope.
More info here

Related

I have to wrap $scope primitives in objects, but can use functions without wrapping them. Why?

This question is inspired by:
ng-model in numeric input does not affect scope
In that question I was surprised to learn that somehow I have nested controllers even though I have exactly one controller per template and I have no custom directives. This might have something to do with Ionic.
I had a problem that I couldn't access primitive $scope properties without wrapping them in objects. I had this problem regardless what I named the primitive property. I could only access it from my template if I wrapped it in an object or referred to $parent.
How come I can't access the primitives regardless of name? I would understand that there can be a naming conflict, but no name worked. And if no name works, then why do primitives wrapped in objects work? Why do I not need to write $parent.object in my template to access an object attached to my $scope?
Accessing vs Defining values
When accessing a value (such as using {{ value }}, or method()), the application will climb up parent scopes looking for the value until it reaches $rootScope: the first version found, if any, is then used.
When defining a value, however (such as using value = 'foo'), it applies it to the current scope, even if a value exists with the same key under a parent scope.
Defining primitives vs defining object properties
When setting a primitive value such as 'value', you're simply defining what value is:
value = 123;
But when defining the property of an object, the object has to be accessed before the property change can be applied to it:
object.value = 123;
The above code looks for object, and applies the change to its value property. If object doesn't exist in the current scope, the application will search up the parent scopes to find object, so it can access it. Then, the change to value is applied to that instance of object. If object doesn't exist at all, the code will throw an error because it is essentially the below:
null.value = 123;
Example
Similar functionality can be seen in vanilla Javascript through prototypal inheritance.
var parent = { primitive: 123, object: { value: 123 } };
var child = Object.create(parent);
console.log(child.primitive); // 123, from parent
console.log(child.object.value); // 123, from parent
child.primitive = 456; // modifies own primitive key
console.log(child.primitive); // 456, from self
console.log(parent.primitive); // still 123
child.object.value = 456; // modifies parent's object's value
console.log(child.object.value); // 456, from parent
console.log(parent.object.value); // 456, since it was modified above
JavaScript objects are dynamic "bags" of properties (referred to as
own properties). JavaScript objects have a link to a prototype object.
When trying to access a property of an object, the property will not
only be sought on the object but on the prototype of the object, the
prototype of the prototype, and so on until either a property with a
matching name is found or the end of the prototype chain is reached.
Read more here
You need to know the prototype inheritance of javascript first.
Then understand how scope works with this very helpful guide.

Why do variables become keys on the window object?

I've noticed that variables become keys and their values become values of said keys when I am writing on the window object. For instance:
var variable = "value";
console.log(window.variable);//logs "value"
But if I create a new object of my own like:
var o = {
key: "value",
var key2 = "value2"
}; // result: "syntax error"
I have to use the colon to set key/value pairs. And even with constructor functions, although I don't get a syntax error, I don't seem to be able to do this inside the constructor function or during object instantiation or after:
function Blueprint(){
this.key = "value";
var key2 = "value2";
}
var o = new Blueprint;
console.log(o.key); //result: "value"
console.log(o.key2); //result: "undefined"
Now, I understand why, in the former case, I generated a syntax error and why in the latter case I was unable to attach key2 to the Blueprint constructor function but what I don't understand why on the global window object I am given free range to just add properties using variables and they instantly become keys on the window object. Other than on the windows object, variables seem to behave as though they are independent of objects and property creation.
I've also noticed similar behavior on the global object when experimenting with node. Are global objects the only objects that can set key/value pairs in this way using variables? And if so, why? Why aren't we forced to do something like the following when working on the global level:
this.key = "value";
Or...
window.key = "value";
Is this just some caveat to the behavior of the language overall or is there some internally consistent logic about the window object in relation to all other objects that explains this?
Global variables (as opposed to local variables declared within a function) become properties of the global object.
In a browser, the global object is window.
The global environment is a so called Object Environment (Record):
Each object environment record is associated with an object called its binding object. An object environment record binds the set of identifier names that directly correspond to the property names of its binding object.
As you can see, the behavior to create properties for identifiers is explicitly defined. And in the case of the global environment, the binding object is the global object, which is window in browsers.
More detailed information about environments can be found in the spec:
There are two kinds of Environment Record values used in this specification: declarative environment records and object environment records. Declarative environment records are used to define the effect of ECMAScript language syntactic elements such as FunctionDeclarations, VariableDeclarations, and Catch clauses that directly associate identifier bindings with ECMAScript language values. Object environment records are used to define the effect of ECMAScript elements such as Program and WithStatement that associate identifier bindings with the properties of some object.
I can't tell you why these two types exist. If you want to know that, you could ask at http://esdiscuss.org/

Update parent scope variable in AngularJS

I have two controllers, one wrapped within another. Now I know the child scope inherits properties from the parent scope but is there a way to update the parent scope variable? So far I have not come across any obvious solutions.
In my situation I have a calendar controller within a form. I would like to update the start and end dates from the parent scope (which is the form) so that the form has the start and end dates when submitted.
You need to use an object (not a primitive) in the parent scope and then you will be able to update it directly from the child scope
Parent:
app.controller('ctrlParent',function($scope){
$scope.parentprimitive = "someprimitive";
$scope.parentobj = {};
$scope.parentobj.parentproperty = "someproperty";
});
Child:
app.controller('ctrlChild',function($scope){
$scope.parentprimitive = "this will NOT modify the parent"; //new child scope variable
$scope.parentobj.parentproperty = "this WILL modify the parent";
});
Working demo: http://jsfiddle.net/sh0ber/xxNxj/
See What are the nuances of scope prototypal / prototypical inheritance in AngularJS?
There is one more way to do this task and to not use the $scope.$parent variable.
Just prepare a method for changing the value in parent scope and use it in child one. Like this:
app.controller('ctrlParent',function($scope) {
$scope.simpleValue = 'x';
$scope.changeSimpleValue = function(newVal) {
$scope.simpleValue = newVal;
};
});
app.controller('ctrlChild',function($scope){
$scope.changeSimpleValue('y');
});
It also works and give you more control over the value changes.
You can then also call the method even in HTML like: <a ng-click="changeSimpleValue('y')" href="#">click me!</a>.
This also works (but not sure whether this follows best practice or not)
app.controller('ctrlParent',function($scope) {
$scope.simpleValue = 'x';
});
app.controller('ctrlChild',function($scope){
$scope.$parent.simpleValue = 'y';
});
When you assign a primitive attribute to a scope, it is always local to the scope (possibly created on the fly), even if a parent scope has an attribute with the same name. This is a design decision, and a good one IMHO.
If you need to change some primitive (ints, booleans, strings) in the parent scope, from the view, you need it to be an attribute of another object in that scope, so the assignment may read:
<a ng-click="viewData.myAttr = 4">Click me!</a>
and it will, in turn:
get the viewData object from whatever scope it is defined in
assign 4 to its myAttr attribute.
For accessing variables declared in the parent, we should use $parent in child controller or template file
In controller
$scope.$parent.varaiable_name
In html template
ng-model="$parent.varaiable_name"

prevent access with setter

The problem is to prevent overriding from outside of a object.
my idea is to use setters:
var spot = function(val){
this.id = val;
this.__defineSetter__("id", function(val){alert("bang");});
}
The id should be set once in the constuctor and never be changed.
I would also like to define the setter on the prototype because I have 10.000 spots. But in this case the setter prevets access also to the consructor.
I am not willing to use var id in the constructor and define a getter on it. In this case every single one of the (10.000) objects has its own closure.
A second question is: Can the setter somehow know wether the var is changed from the constructor or later from a (spot)internal function? So I could prevent access just from outside the object.
Firstly, that code is non-standard and deprecated. Depending on the platform you're developing for, I suggest using preventExtensions or freeze. Or maybe you just want to define the id property as read-only, in which case use defineProperty.
Secondly, as you've done things, each instance of spot already has it's own closure. That setter that you defined is a unique function + lexical scope that will exist for every spot, aka closure. So I'm not sure what the big deal is with making a getter as well. Although the first point trumps here, and you should be using the new methods to achieve that functionality.
Lastly, no matter what direction you take, the setter will not be able to "know" anything. So you can either have a separate variable, something to the tune of isInitialised, or create a code invariant so that the value is undefined at first, and numeric afterwards, which allows the simple check if the variable has a defined value, in which case it's been set, and shouldn't set it again.

change JavaScript scope

Is there any posibility to exchange the special global window scope by a custom one? I just thought with is meant to, but it only stacks another "lookup" scope. Eg.
test={};
with(test){
a=1;
}
does not create the property test.a but window.a.
So the window object has a JS-specific special quality I cannot recreate with own code?
If the property exists on the object given to with then it will be modified, but it will never be created. This is a major "gotcha" with using with and the primary reason it should be avoided.
With with only if the object passed in has that property, will it be modified. It will not be created.
http://www.yuiblog.com/blog/2006/04/11/with-statement-considered-harmful/

Categories