In my previous article, I described the basics of AngularJs. You can read it from the following link.
AngularJS - Basics: Part 2
Scopes
A scope is an object that refers to the application model. Pretty vague, right? I'll try to clarify it.
Scopes are the object that provides context to evaluate expressions inside an application model. To be clearer, {{username}} in a template is totally useless unless we have a specific scope object that defines the "username" property. Then only, {{ username }} can be evaluated against this scope object.
The scope has certain characteristics like $watch, $apply, and $digest. What do they do?
- $watch: It keeps a constant eye on any mutation that occurs inside the model.
- $apply: It notifies Angular to update the DOM model after the scope properties are updated.
- $digest: It is triggered right after $apply does its job. Its task is to notify all the $watches that the application model has been updated.
It basically works as the glue between the controller and the view, without which, these two cannot communicate or notify each other about changes that are to be reflected. Both the controller and the directive have a reference to scope.
Hierarchy
Every Angular application has only one root scope and may contain several child scopes.
Scopes are arranged in a hierarchical order that is the same as the order of elements in the DOM tree.
When Angular evaluates an expression, it first looks at the scope associated with the current element for any such defined property. If not found, it looks up to the scope of the parent element in the DOM tree and so on until the root scope is reached.
For example
<div ng-controller="oneCtrl">
{{ username }}
<div ng-controller="twoCtrl">
{{ username }}
</div>
</div>
<script>
function oneCtrl($scope) {
$scope.username = "abc";
}
</script>
This will evaluate both of the {{ username }} expressions since the "username" property is well-defined in the parent scope.
To limit their access to other application components, scopes can be nested.
For example
<div ng-controller="oneCtrl">
{{ username }}
</div>
<div ng-controller="twoCtrl">
{{ username }}
</div>
<script>
function oneCtrl($scope) {
$scope.username = "abc";
}
</script>
This time, Angular will not evaluate the second expression under the controller "twoCtrl". The reason is simple, the "username" property is not defined for the scope of the current element. For this to work, we'll need to add.
function twoCtrl($scope) {
$scope.username = "xyz";
}
Life Cycle
To work well with the scopes, one must have an understanding of all the phases that a scope goes through, during its life cycle.
- Creation: A root scope is created by Angular during the application bootstrap. When the templates are linked to the custom directives, some new child scopes may be created.
- Watcher Registration: When a template is linked, the directives that create scope register their watches on the parent scope. This helps them to watch for and propagate any model change from view to the directive.
- Model Mutation: While making any changes to the model, they should be done within $scope.$apply()
- Mutation Observation: When $apply ends, a $digest cycle is performed on the root scope that propagates through all the child scopes. All of the watching expressions are checked for any changes and the scope calls the listener callback when they are changed.
- Destruction: When the child scope is not needed any longer, the creator of the child scope must call $scope.$destroy() to clean up the child scope. The objective is to stop the $digest cycle propagation into the child's scope and free the memory when they are no longer needed.
Directives and Scopes
There are cases when directives create their own scopes. For instance, "ng-controller" and "ng-repeat" create their own child scopes and attach them to the DOM element. But before I get any deeper into it, we all should understand controllers and how to make their best use in applications.
My next extension in this series will talk mainly about controllers.