AngularJS - Isolated Scope

On our way down the directive path, we can't afford to overlook the concept of Isolated Scopes and how useful they are.
 
Let's set up an example of a directive with a text box bound to a model whose value is shown right next to it. It will look something like:

  1. <div ng-app="myApp">  
  2.     <kid></kid>  
  3. </div> 
  1. var app = angular.module('myApp', []);  
  2.   
  3. app.directive("kid"function() {  
  4.     return {  
  5.         restrict: 'E',  
  6.         template: '<input type="text" ng-model="task" /> {{ task }} <br/>'  
  7.     };  
  8. }); 
This will work simply as expected. You write the task for a kid in the text box and the task will be shown along the side of the text box.
 
 The question I'll put up is: What happens if we include multiple components "kid" in our template?
 
 That's right, we'll see multiple text boxes as the result.
 
 But one thing that you may not want is: When we write a task for a kid into the text box, the other kids also get the same message. See: jsfiddle
 
 Technically, the "task" model is in the parent scope and it binds to the "task" model of each directive with the same value.This is called polluting the parent scope.
 
 The entire purpose of re-using the directives must have been lost if not for the Isolated Scopes. I tried to explain their role in the earlier article. All we must do is create a space for this directive's own scope in which it will maintain some properties and have nothing to do with the parent scope or the scope of other directives. This can be done by assigning an empty object as the scope of our directive. This example explains how its done: jsfiddle
 
 This isolated scope again comes with a problem that you may have already thought of. It cuts the connection of the directive's scope with its parent scope. What good is that? None!
 
 As a solution to this problem, Angular provides us with the option to bind our isolated scope's expressions and objects with those of the parent scope. This can be done in several ways, on one of which I am going to throw some light on. Let's make our directives communicate back to their controllers.
 
 Isolated scope "&"
 
 First set up the controller in which we intend to place our directives. This controller will contain a function that in turn will be called by our directive. Let's see how: 
  1. var app = angular.module('myApp', []);  
  2.   
  3. app.controller('taskCtrl'function ($scope) {  
  4.     $scope.logTask = function (task) {  
  5.         alert(task + " is done!");  
  6.     };  
  7. });  
  8.   
  9. app.directive("kid"function () {  
  10.     return {  
  11.         restrict: 'E',  
  12.         scope: {  
  13.             done: '&done'       // '&' is used for referencing the expressions  
  14.         },  
  15.         template: '<input type="text" ng-model="task" /> {{ task }}<br/>'  
  16.         +'<button ng-click="done({task:task})">I\'m done!</button><br/>'  
  17.     };  
  18. }); 
  1. <div ng-app="myApp">  
  2.     <div ng-controller="taskCtrl">  
  3.         <kid done="logTask(task)"></kid>  
  4.     </div>  
  5. </div> 
In this code snippet, you will notice some more new things. In the template, we pass on an attribute named "done" that holds the reference to the controller method "logTask". When declaring an isolated scope, we simply bind this "done" attribute's value to a scope property that is also named "done". If both the attribute name and the scope variable name are the same then we can use this shorthand:
  1. done: '&' 

Once this attribute value, that holds the reference to the controller scope method, is bound to the scope property of the directive, we can simply use it in our directive's template. You can see how: jsfiddle

We shall now discuss other ways of binding data from a controller to an isolated scope. We will try to understand the difference among them and when to use them.

Isolated Scope “@”


Let's talk about setting up the isolated scope by data binding using the “@” sign. This tell the scope property of the directive to evaluate the attribute value as a string.

Anything that we pass on to the attribute will be evaluated as a string and assigned to the scope property of the directive. The following is how it's done:

  1. <div ng-app="myApp">  
  2.     <div ng-controller="taskCtrl">  
  3.         <div kid name="{{var}}"></div>  
  4.     </div>  
  5. </div> 
  1. var app = angular.module('myApp', []);  
  2.   
  3. app.controller('taskCtrl'function ($scope) {  
  4.     $scope.var = "Shashank";  
  5. });  
  6.   
  7. app.directive("kid"function () {  
  8.     return {  
  9.         scope: {  
  10.             name: '@'  
  11.         },  
  12.         template: '{{name}}'  
  13.     };  
  14. }); 
Here, the name property of the directive's scope is bound to a string that was evaluated from the "name" attribute of the element. This is referred to as "one-way" binding.

See the working example: jsfiddle

Isolated Scope “=”


There might be cases when you don't just want to pass the string to the directive. You can pass an object into the directive, that updates inside the directive as soon as it is updated in the controller. But "@" can also do this for sure. What it can not do is bind the data another way round. I mean, when that object is updated inside the directive, "@" cannot update that object back into the controller. This can be done by "=". One thing you should understand is: "=" doesn't expect the controller's scope property to be enclosed within {{ }}. These brackets tell Angular to evaluate the enclosed property as a string. So we will remove these brackets when using "=" to bind data from the controller to the directive.
  1. <div ng-app="myApp">  
  2.     <div ng-controller="taskCtrl">  
  3.         Ctrl: <input type="text" ng-model="var">  
  4.         <div kid name="var"></div>  
  5.     </div>  
  6. </div> 
  1. var app = angular.module('myApp', []);  
  2.   
  3. app.controller('taskCtrl'function ($scope) {  
  4.     $scope.var = "Shashank";  
  5. });  
  6.   
  7. app.directive("kid"function () {  
  8.     return {  
  9.         scope: {  
  10.             name: '='  
  11.         },  
  12.         template: 'Dir: <input type="text" ng-model="name">'  
  13.     };  
  14. }); 

This example shows two text boxes, the first one with the scope property "var" of the controller scope. The second one has the isolated scope property "name" of the directive. These two are bound to each other using the "=" operator and it enables us to change any of the text box's content to see the updated binding in the other. This is often called "two-way binding". See it working here: jsfiddle

Previous article: AngularJS - Directives (Contd.)


Similar Articles