Getting Started With AngularJS – Scope

In this blog post we will see in more detail about angular $scope variable


We use $scope to bind data between angular and DOM. $scope is injected to a controller using Dependency Injection.
$scope is local to each controller, i.e it is not shared between controllers.

$rootScope

$rootScope is the top level scope object and all other scopes created are child to this scope. These is only a single $rootScope in a single angular application. Lets see a working example of $rootScope and how its shared between 2 controllers.

var mainMod = angular.module('MainApp', []);
mainMod.controller('MainCtrl', ['$scope','$rootScope',
    function ($scope,$rootScope) {
      $scope.update = function(){
        $rootScope.text2 = $scope.text1;
      }
    }
]);

mainMod.controller('MainCtrl2', ['$scope','$rootScope',
    function ($scope,$rootScope) {
      $scope.update = function(){
        $rootScope.text1 = $scope.text2;
      }
    }
]);

Our HTML code

<body ng-app='MainApp'>
    <div ng-controller='MainCtrl'>
      <div>
        <div>Controller1</div>
        <input type='text' ng-model='text1'/>
        <button type='button' ng-click='update();'>Update</button>
      </div>
    </div>
    <div ng-controller='MainCtrl2'>
      <div>
        <div>Controller2</div>
        <input type='text' ng-model='text2'/>
        <button type='button' ng-click='update();'>Update</button>
      </div>
    </div>
</body>

In the above example, if type text in first controller and press ‘Update’ button it gets reflected to the second controller as well. Using this we see that $rootScope is common and shared between all controllers.

More details here

$watch

$scope and $rootScope provide a $watch function. $watch function is used to listen for any changes in $scope variables. More details here Lets extend the above example and see its working

var mainMod = angular.module('MainApp', []);
mainMod.controller('MainCtrl', ['$scope','$rootScope',
    function ($scope,$rootScope) {
      $scope.$watch('text1',function(newValue,oldValue){
        if(newValue){
          $rootScope.text2 = newValue;
        }
      });
    }
]);

mainMod.controller('MainCtrl2', ['$scope','$rootScope',
    function ($scope,$rootScope) {
      $scope.$watch('text2',function(value){
        if(value){
            $rootScope.text1 = value;
        }
      });
    }
]);
<body ng-app='MainApp'>
    <div ng-controller='MainCtrl'>
      <div>
        <div>Controller1</div>
        <input type='text' ng-model='text1'/>
      </div>
    </div>
    <div ng-controller='MainCtrl2'>
      <div>
        <div>Controller2</div>
        <input type='text' ng-model='text2'/>
      </div>
    </div>
</body>

Now if you type in Controller1, Controller2 get automatically updated. This is because using $watch we keep on updating the $rootScope.

$apply

There might be many instances, when value of a scope variable might change outside of angular scope. e.g browser dom events, jquery document ready event, setTimeout, 3rd party libraries or any other such external factors. To sync the $scope back we use $apply function. Lets demonstrate this using an example

var mainMod = angular.module('MainApp', []);
mainMod.controller('MainCtrl', ['$scope','$rootScope',
    function ($scope,$rootScope) {
      $scope.update = function(){
        setTimeout(function(){
          $scope.text1 = 'New Text';
          $scope.$apply(); //removing this New Text won't be updated
        },500);
      }
    }
]);
<body ng-app='MainApp'>
    <div ng-controller='MainCtrl'>
      <div>
        <div>Controller1</div>
        <input type='text' ng-model='text1'/>
        <br/>
        Text: {{text1}}
        <br/>
        <input type='button' ng-click='update();' value='Update Outside' />
      </div>
    </div>
</body>

In the above example, setTimeout is used simulate an event which is outside angular $scope. If in the above example we remove $scope.$apply() the text is not updated.

More details here

$scope Events $on,$emit,$broadcast

$scope and $rootScope object exposes many events which we can use to transmit data.

$on: This is used to listen to any event fired.

$scope.$on('test_event',function(event){
});

$emit: This is used to fire an event to all its parent scopes, till the $rootScope

$scope.$emit('test_event');

$broadcast: This is used to fire an event to all its child scopes.

$scope.$broadcast('test_event');

Let see an example to see how these work.

var mainMod = angular.module('MainApp', []);
mainMod.controller('MainCtrl', ['$scope',
    function ($scope) {
      $scope.name = 'Ctrl1';
      $scope.update = function(){
        $scope.$emit('emit_event');
      }
      $scope.update2 = function(){
        $scope.$broadcast('broadcast_event');
      }
      $scope.$on('emit_event',function(event){
         $scope.event = 'emit event from ' + event.targetScope.name
     })
     $scope.$on('broadcast_event',function(event){
         $scope.event = 'broadcast event from ' + event.targetScope.name
     })
    }
]);
mainMod.controller('ChildCtrl',function($scope){
      $scope.name = 'Ctrl2';
      $scope.update = function(){
        $scope.$emit('emit_event');
      }
      $scope.update2 = function(){
        $scope.$broadcast('broadcast_event');
      }
     $scope.$on('emit_event',function(event){
         $scope.event = 'emit event from ' + event.targetScope.name
     })
     $scope.$on('broadcast_event',function(event){
         $scope.event = 'broadcast event from ' + event.targetScope.name
     })
});
<body ng-app='MainApp'>
    <div ng-controller='MainCtrl'>
      <div>
        <div>Controller1</div>
        Event: {{event}}
        <br/>
        <input type='button' ng-click='update();' value='Emit Event' />
        <input type='button' ng-click='update2();' value='Broadcast Event' />
        <br/>
         <div ng-controller='ChildCtrl'>
           <div>Child Controller</div>
           Event: {{event}}
           <br/>
           <input type='button' ng-click='update();' value='Emit Event' />
           <input type='button' ng-click='update2();' value='Broadcase Event' />
        </div>
      </div>
    </div>
</body>

In the above example, we create 2 controllers one controller is child of another. $scope also becomes child of another and we can clearly see difference between $emit and $broadcast.
All these event functions are also available on $rootScope as well.

$rootScope $emit/$broadcast

As we saw above $emit is used to send data upwards and $broadcast is used to send data downwards. So in $rootScope, $emit won’t make any sense since there is no parent to $rootScope. But there is a small difference in $rootScope.$emit, it only send events to $rootScope.$on listeners. If you used $rootScope.$broadcast, it send event to all listeners ($rootScope.$on and $scope.$on)

We can see more detailed explanation about Scopes here