Scope in AngularJS For Beginners

Scope is a JavaScript object that refers to the application model. It acts as a context for evaluating Angular expressions. Basically, it acts as the glue between a controller and a view. Scopes are hierarchical in nature and follow the DOM structure of your AngularJs app.


Figure 1: View Controllers and scope

Scope is a special JavaScript object that plays the role of joining a controller with views. Scope contains the model data. In controllers, model data is accessed via the $scope object.

The $scope object is the connecting bridge between the controller and the view. In a controller the scope value is set from the services or manually and in the view. We generally read the value or manipulate it. $scopes are the data source for any view or UI pages in an AngularJs application. The two-way binding feature allows the $scope object to be modified when the View changes and the view is modified when $scope changes its value.

Example 1

  1. <!DOCTYPE html>  
  2. <html lang="en" ng-app="mainApp">  
  3. <title>AngularJS First Application</title>  
  4.   
  5. <head>  
  6.     <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>  
  7.   
  8.     <script>  
  9.         var mainApp = angular.module("mainApp", []);  
  10.         mainApp.controller("TestController", function ($scope) {  
  11.             $scope.message = "Welcome User";  
  12.         });  
  13.     </script>  
  14. </head>  
  15.   
  16. <body ng-app="mainApp">  
  17.     <div ng-controller="TestController">  
  18.         <input type="text" name="txt"  ng-model="test" />  
  19.   
  20.         {{test}}  
  21.         <h1>Hello ,{{ message }}</h1>  
  22.   
  23.     </div>  
  24. </body>  
  25. </html> </body>  
  26. </html>  

The following are the important points to be considered in the preceding example.

  • $scope is passed as the first argument to the controller during its constructor definition.
  • $scope.message is the model that is to be used in the HTML page.
  • We've set values to models that will be reflected in the application module whose controller is TestController.
  • We can define functions as well in $scope.

We saw in the example that as soon as the TextBox is changed we see the label text also changed.

In earlier JavaScript we used to on text changed or onblur event we need to capture the TextBox value and assign it to the label text.

How this happen automatically here

Scope has certain characteristics like $watch, $apply and $digest. Angular defines a concept called digest cycle. This cycle can be considered as a loop, during that Angular checks if there are any changes to all the variables watched by all the $scopes.

$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.

Let us do a hands-on sample to understand it better.

$watch(): $watch helps to listen for $scope change. Angular creates a $watch for every binding on the screen. That includes both ng-model and {{ ... }} (and ng-repeat and others directives).

Consider one example here.

We have a text box for changing some value in the TextBox. Then the $scope.$watch function is called and gives an alert message.

Example 2
  1. <html>  
  2. <head>  
  3.     <title>AngularJS Watch</title>  
  4.     <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>  
  5.     <script>  
  6.         var myapp = angular.module("myapp", []);  
  7.   
  8.         function MyController($scope) {  
  9.             $scope.$watch('myValue', function () {  
  10.                 alert('hey, 'myValue'has changed!');  
  11.             });  
  12.         }  
  13.     </script>  
  14. </head>  
  15. <body ng-app="myapp" ng-controller="MyController">  
  16.   
  17.     <div style="border: 5px solid gray; width: 400px;">  
  18.         <label><b>Calling the Watch Function</b></label>  
  19.         <input type="text" ng-model="myVar" />  
  20.     </div>  
  21. </body>  
  22. </html>  
 
 Figure 2: Run the Code 
  • The second argument passed to $watch () is known as a listener function and is called whenever the value of a Model changes.
  • A watcher can evaluate any value.
  • A watcher's handler can execute anything when the value mentioned previously has changed.
  • All watchers are evaluated when $digest () is called.
$digest(): checks the registered watchers  
  • The $digest checks the $watch the directive registered, sees the change and fires the handler associated with it, updating the DOM element. The digest call will make sure that the UI and the JavaScript code remains synced by evaluating every watcher attached to all the $scopes as long as nothing changes. If no more changes happen in the digest loop, then it's considered to be finished.
  • The $scope.$digest() function iterates through all the watches in the $scope object and its child $scope objects (if it has any). When $digest() iterates over the watches, it calls the value function for each watch. If the value returned by the value function is different than the value it returned the last time it was called, the listener function for that watch is called.
  • The $digest () function is called whenever AngularJs thinks it is necessary. For instance, after a button click handler has been executed, or after an AJAX call returns (after the done () / fail() call back function has been executed).

$apply ()

$ apply helps to apply for a $scope change.

Consider the example here, we want to show the output of the page to be delayed by 3 seconds. We need to define it in scope.apply().

Example 3
  1. <html ng-app="ScopeApplyApp">  
  2. <head>  
  3.     <title>AngularJS Apply</title>  
  4.     <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>  
  5.     <script>  
  6.         angular.module('ScopeApplyApp', []).controller('scopectrl', function ($scope) {  
  7.   
  8.             $scope.getMessage = function () {  
  9.                 setTimeout(function () {  
  10.                     $scope.$apply(function () {  
  11.                          
  12.                         $scope.message = 'Fetched after 3 seconds';  
  13.                         console.log('message:' + $scope.message);  
  14.                     });  
  15.                 }, 2000);  
  16.             }  
  17.             $scope.getMessage();  
  18.         });  
  19.         </script>  
  20. </head>  
  21.    <body ng-app="ScopeApplyApp">  
  22.       <div ng-controller="scopectrl">  
  23.          Message Now : {{message}}  
  24.       </div>    
  25.     </body>  
  26. </html>
 
Figure 3: Search result

$scope.$apply() takes a function or an Angular expression string and executes it, then calls $scope.$digest() to update any bindings or watchers.

If you are changing your model outside of your AngularJs context (or the variable that you want isn't being watched) then you will need to use $scope.$apply in AngularJs as a way of telling it that it will need to fire a new $digest cycle so that it can detect your changes.

The apply() on scope is called automatically by Angular code in many scenarios like ng-click timeout() calls or $http ajax() calls and so on.

The better practice is to call $apply only when you know you are outside of a $digest loop, such as inside the directive link function. That is, keep the $apply out of a $controller that is accessible to the declarative code in HTML or in another controller and do the $apply in the directive link function, when you know you are outside of the $digest loop.

Scope life cycle

Scope data goes through a life cycle when the Angular app is loaded into the browser. Understanding the scope life cycle will help you to understand the interaction between scope and other AngularJs components.

The scope data goes through the following life cycle phases.

Creation: This phase initializes the scope. The root scope is created by the $injector when the application is bootstrapped. During template linking, some directives create new child scopes.

A digest loop is also created in this phase that interacts with the browser event loop. This digest loop is responsible for updating DOM elements with the changes made to the model as well as executing any registered watcher functions.

Watcher registration: This phase registers watches for values on the scope that are represented in the template. These watches propagate model changes automatically to the DOM elements.

You can also register your own watch functions on a scope value using the $watch() function.

Model mutation: This phase occurs when data in the scope changes. When you make the changes in your Angular app code, the scope function $apply() updates the model and calls the $digest() function to update the DOM elements and registered watches.

When you do the changes to scope inside your Angular code like within controllers or services, Angular internally call $apply() function for you. But when you do the changes to the scope outside the Angular code, you need to call $apply() function explicitly on the scope to force the model and DOM to be updated correctly.

Mutation observation: This phase occurs when the $digest() function is executed by the digest loop at the end of $apply() call. When $digest() function executes, it evaluates all watches for model changes. If a value has been changed, $digest() calls the $watch listener and updates the DOM elements.

Scope destruction: This phase occurs when child scopes are no longer needed and these scopes are removed from the browsers memory by using $destroy() function. It is the responsibility of the child scope creator to destroy them via scope.$destroy() API.

This stop propagation of $digest calls into the child scopes and allow the memory to be reclaimed by the browser garbage collector.

Scope characteristics

Scopes provide APIs ($watch) to observe model mutations. Scopes provide APIs ($apply) to propagate any model changes through the system into the view from outside of the "Angular realm" (controllers, services, Angular event handlers).

Scopes can be nested to limit access to the properties of application components while providing access to shared model properties. Nested scopes are either "child scopes" or "isolate scopes". A "child scope" (prototypically) inherits properties from its parent scope. An "isolate scope" does not. See isolated scopes for more information. Scopes provide context against which expressions are evaluated. For example, {{username}} expression is meaningless, unless it is evaluated against a specific scope that defines the username property.

Scope Inheritance

Scopes are controllers specific. If we define nested controllers then child controller will inherit the scope of its parent controller.

Let us do a sample and understand what rootscope and scope are.

Example 4 
  1. <html>  
  2. <head>  
  3.    <title>Angular JS Forms</title>  
  4. </head>  
  5. <body>  
  6.    <h2>AngularJS Sample Application</h2>  
  7.    <div ng-app="mainApp" ng-controller="shapeController">  
  8.       <p>{{message}} <br/> {{type}} </p>  
  9.       <div ng-controller="circleController">  
  10.          <p>{{message}} <br/> {{type}} </p>  
  11.       </div>  
  12.       <div ng-controller="squareController">  
  13.          <p>{{message}} <br/> {{type}} </p>  
  14.       </div>  
  15.    </div>  
  16.    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>  
  17.    <script>  
  18.        var mainApp = angular.module("mainApp", []);  
  19.   
  20.        mainApp.controller("shapeController", function ($scope) {  
  21.            $scope.message = "In shape controller";  
  22.            $scope.type = "Shape";  
  23.        });  
  24.   
  25.        mainApp.controller("circleController", function ($scope) {  
  26.            $scope.message = "In circle controller";  
  27.        });  
  28.   
  29.        mainApp.controller("squareController", function ($scope) {  
  30.            $scope.message = "In square controller";  
  31.            $scope.type = "Square";  
  32.        });  
  33.   
  34.    </script>  
  35. </body>  
  36. </html>  
 
Figure 4: AngularJs Scope 
 
The $rootScope is the top-most scope. An app can have only one $rootScope that will be shared among all the components of an app. Hence it acts like a global variable. All other $scopes are children of the $rootScope.

This is all about the basics of scope in AngularJs. Thanks for reading.