Understanding ng-scope inheritance/hierarchy - angularjs

Just want to understand more about ng-scope inheritance/hierarchy, because i don't exactly understand how it works.
Assume i have a block like this
<div ng-controller="CtrlName">
<div ng-scope-1>
<div ng-scope-2>
<div ng-scope-3>
<div ng-scope-4></div>
</div>
</div>
</div>
Is it correct to assume that from controller's point, to get to
Scope 1, i need to define $scope.scope1 in the controller
Scope 2, i need to define $scope.scope1.scope2 in the controller?
Scope 4, i need to define $scope.scope1.scope2.scope3.scope4 in the controller?
And what if scope 3 block is appended to scope 2 using ngInclude? Does the scope referring changes too?
I asked this because of this one question here - AngularJs: why doesn't ng-switch update when I use ng-click?
And the solution seems to mention about scope inheritance. Not sure if the example i gave above is related to the scope inheritance too.

Related

Global function with $rootScope (angularJS) [duplicate]

This question already has answers here:
What are the nuances of scope prototypal / prototypical inheritance in AngularJS?
(3 answers)
Closed 5 years ago.
I have a general template for all pages which contains a menu bar and it is outside the ng-view.From one of my page which is inside the ng-view i want to bind input data to this template area(this area is under a different controller than the input page).I mean when i will enter name,the name will appear to the template area.Is it possible ?
Here is the plunker
<body ng-app="sampleApp">
<div class="container">
<div class="row">
name is :{{name}}<br/>
username is :{{uname}}
<div class="col-md-3">
<ul class="nav">
<li> Add name </li>
<li> add username </li>
</ul>
</div>
<div class="col-md-9">
<div ng-view></div>
</div>
</div>
</div>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script src="app.js"></script>
</body>
This issue with primitives can be easily avoided by following the "best practice" of always have a '.' in your ng-models – watch 3 minutes worth. Misko demonstrates the primitive binding issue with ng-switch.
Having a '.' in your models will ensure that prototypal inheritance is in play. So, use
<input type="text" ng-model="someObj.prop1"> rather than
<input type="text" ng-model="prop1">.
— AngularJS Wiki - What are the nuances of scope prototypal / prototypical inheritance?
The DEMO on PLNKR
$scope.obj is working like a $rootScope variable. Is it for prototypal inheritance?
Scopes are arranged in hierarchical structure which mimic the DOM structure of the application. Each AngularJS application has exactly one root scope, but may have any number of child scopes.
ng-app --> $rootScope
|- ng-controller --> $scope (container)
|- ng-view --> $scope (view)
By using: <input ng-model="obj.name" /> the ng-model directive in the view controller uses prototypical inheritance to find $scope.obj from outside the view. It can then get and set the name property of that object.
For more information, see AngularJS Developer Guide - Scope Hierarchies
$rootScope exists, but it can be used for evil
Scopes in AngularJS form a hierarchy, prototypically inheriting from a root scope at the top of the tree. Usually this can be ignored, since most views have a controller, and therefore a scope, of their own.
Occasionally there are pieces of data that you want to make global to the whole app. For these, you can inject $rootScope and set values on it like any other scope. Since the scopes inherit from the root scope, these values will be available to the expressions attached to directives like ng-show just like values on your local $scope.
Of course, global state sucks and you should use $rootScope sparingly, like you would (hopefully) use with global variables in any language. In particular, don't use it for code, only data. If you're tempted to put a function on $rootScope, it's almost always better to put it in a service that can be injected where it's needed, and more easily tested.
Conversely, don't create a service whose only purpose in life is to store and return bits of data.
— AngularJS FAQ - $rootScope exists, but it can be used for evil
Angular's $rootScope can be used to share information between the app's components (besides other uses). It is discouraged to rely upon it too much because it could become polluted or difficult to trace up and down the app's entire scope 'stack'. But if you really need or want to set 'global' data, it works:
In your new plunkr, you are using both ng-model and ng-value for the text input. Remove the ng-value altogether. (It is used typically for elements that have 'value' properties, like radio buttons and checkboxes, where the 'value' is 'checked' or 'selected', etc.) ng-model is what you want.
http://plnkr.co/edit/DnzOdRicXLHtg4DqoVdJ?p=preview
name is :{{$root.name}}
username is :{{$root.uname}}
and
Name: <input ng-model="$root.name">
<h1>You entered: {{$root.name}}</h1>

In AngularJS, we can inherit scope, but new value in inner scope creates a new variable?

We can inherit a scope (or I may think of it as the usual outer scope / inner scope scenario):
Same controller:
https://jsfiddle.net/fk4hzvxw/
Different controllers:
https://jsfiddle.net/fk4hzvxw/1/
<div ng-app="myApp">
<div ng-controller="hihi">
<div ng-init="ha = 42"></div>
<input type="text" ng-model="ha"> {{ ha }}
<div ng-controller="hello">
<input type="text" ng-model="ha"> {{ ha }}
</div>
</div>
</div>
So I think, it is said that, controller "hihi" creates a scope, and controller "hello" creates also a scope, and inherits the scope created by controller "hihi". (Or I might think of it as just outer scope and inner scope like in a traditional program -- is this a correct way to think about it?)
We can type into the first input box, and all values on the screen update to the same value. However, when we type into the second input box, then the second variable seems to "take off as its own". Now there are 2 variables ha, one in the outer scope, one in the inner scope, as we type into the 2 input box, they affect only its own scope's variable.
How should we think about this? In traditional programming, this won't happen. Also, what are the implications of this?
This can happen in traditional programming. Think of the scopes as a series of Javascript objects where each object has the parent scope as its prototype. Whenever you access an attribute of an object the prototype chain is searched until a match is found, but assigning to an attribute simply assigns it on the object itself.
In other languages you might see something similar, e.g. in Python accessing an attribute on a class searches all the parent classes but assigning it on an object hides all the parent values.
The way to avoid this in Angular is to always use the 'controller as' syntax. This gives every controller a name and you can then be sure which one will have its model updated.
<div ng-app="myApp">
<div ng-controller="hihi as hihi">
<div ng-init="ha = 42"></div>
<input type="text" ng-model="hihi.ha"> {{ hihi.ha }}
<div ng-controller="hello as hello">
<input type="text" ng-model="hello.ha"> {{ hello.ha }}
</div>
</div>
</div>
But then inside the controller code you access the attributes through this instead of using $scope (and there's also a convention that you do var vm=this; at the top of the controller and use vm to avoid problems with this varying in nested functions).
(Or I might think of it as just outer scope and inner scope like in a
traditional program -- is this a correct way to think about it?)
Yup. This has more to do with how JavaScript works(the scope chain) than with AngularJS.
When the JavaScript interpreter comes across a variable, it searches for it in the current scope. If it doesn't find it, it moves to the outer scope of the current scope and keeps on going up the scope chain until the variable is found.
You can read up on the JavaScript scope chain here.

angular scope of ng-model to filter model of a different scope

Reading the docs tutorial, under Template. The code below is taken from app/index.html
Does angular allows cross visibility of sibling scopes?
How is it that the data named query which is is in scope 1 is available to the filter in scope 2 which is a sibling and not a parent scope? Thanks.
<div class="container-fluid">
<div class="row">
<div class="col-md-2"> //-----------scope 1 -----------------
<!--Sidebar content-->
Search: <input ng-model="query">
</div>
<div class="col-md-10"> //-----------scope 2 -----------------
<!--Body content-->
<ul class="phones">
<li ng-repeat="phone in phones | filter:query">
{{phone.name}}
<p>{{phone.snippet}}</p>
</li>
</ul>
</div>
</div>
</div>
There is a $scope tree, starting from $rootScope. In your HTML, there is only 1 scope in play (ignoring ngRepeat, which creates its own child scope with each iteration). So when a user enters a value in the input box, it is being bound to query which resides in the same scope as the sibling div. Hence why both query is bound to the same scope variable, even though they are within sibling divs.
One way to break the binding is to introduce a child scope for your input control. For example, ngController will create a child scope that inherits from the parent scope. As soon as a key is pressed, it creates a copy of query in the child scope. This breaks two-way binding because now two copies of the query variable exist on different scopes.
<div class="col-md-2" ng-controller="anyCtrl"> //-----------scope 1 -----------------
<!--Sidebar content-->
Search: <input ng-model="query">
</div>
You should visualize your $scope tree and get to know which directives will create child scopes. Also keep in mind that $scope variable lookups (reads) are resolved through prototypical $scope inheritance (meaning the definition of the $scope variable could exist higher up the $scope tree). But $scope variable writes will write to its immediate scope - sometimes breaking the apparent binding as I've explained above.

AngularJS : Correct usage of ng-model

I was reading Angular js docs when I came across the issues mentioned related to using ng-model with directives like ng-include , ng-switch , and ng-view.The reason mentioned was child scope and parent scope but I was not able to understand it completely.
Also it was mentioned that issue with primitives can be easily avoided by following the "best practice" of always have a '.' in your ng-model.
Here's the link
Can anyone please explain it in less-technical language?
ng-include, ng-switch and ng-if creates child scope.
It means that if you create
<div ng-controller="MyCtrl">
<div id="innerDiv" ng-if="!something">
<input ng-model="model">
</div>
</div>`
the model will be created in scope created by the #innerDiv (because of using ng-if). It might be problematic if you want to use model value in the controller, because it won't be possible (Parent scope has no access to properties of child scope!).
The solution is to use $parent.model in <input ng-model="model">. Then the property will be changed in parent's scope and that is what you usually want to achieve.
However using $parent might look not good for someone, so better solution is to create a named model in the controller. The you can use it and follow the rule "always have a '.' in your ng-model." Child scope created by ng-if has access to parrent scope, so he can use already defined $scope.model and change $scope.model.text property.
Controller:
$scope.model = {};
Html:
<div ng-controller="MyCtrl">
<div id="innerDiv" ng-if="!something">
<input ng-model="model.text">
</div>
</div>`
(But remember that if you not define $scope.model in the controller, it would behave like in first example).
If you are not sure that you are in the same scopes, you can check it by displaying scope's $id. Simply add in html {{$id}} above ng-if (or ng-include, ng-switch) and inside.
<div ng-controller="MyCtrl">
scope id: {{$id}}
<div id="innerDiv" ng-if="!something">
child scope id:{{$id}}
</div>
<div>scope id again {{$id}}</div>
</div>`
Some examples:
https://jsfiddle.net/La90btfh/3/

Difference between onLoad and ng-init in angular

I am learning angular. I don't understand what is difference between onLoad and ng-init for initialization of a variable. In which scope it creates this variable.
For example
<ng-include onLoad="selectedReq=reqSelected" src="'partials/abc.html'"></ng-include>
OR
<ng-include ng-init="selectedReq=reqSelected" src="partials/abc.html"></ng-include>
Please also give me some idea about isolated scope.
ng-init is a directive that can be placed inside div's, span's, whatever, whereas onload is an attribute specific to the ng-include directive that functions as an ng-init. To see what I mean try something like:
<span onload="a = 1">{{ a }}</span>
<span ng-init="b = 2">{{ b }}</span>
You'll see that only the second one shows up.
An isolated scope is a scope which does not prototypically inherit from its parent scope. In laymen's terms if you have a widget that doesn't need to read and write to the parent scope arbitrarily then you use an isolate scope on the widget so that the widget and widget container can freely use their scopes without overriding each other's properties.
From angular's documentation,
ng-init SHOULD NOT be used for any initialization. It should be used only for aliasing.
https://docs.angularjs.org/api/ng/directive/ngInit
onload should be used if any expression needs to be evaluated after a partial view is loaded (by ng-include).
https://docs.angularjs.org/api/ng/directive/ngInclude
The major difference between them is when used with ng-include.
<div ng-include="partialViewUrl" onload="myFunction()"></div>
In this case, myFunction is called everytime the partial view is loaded.
<div ng-include="partialViewUrl" ng-init="myFunction()"></div>
Whereas, in this case, myFunction is called only once when the parent view is loaded.
Works for me.
<div ng-show="$scope.showme === true">Hello World</div>
<div ng-repeat="a in $scope.bigdata" ng-init="$scope.showme = true">{{ a.title }}</div>

Resources