[angularjs] How do I use $scope.$watch and $scope.$apply in AngularJS?

AngularJS extends this events-loop, creating something called AngularJS context.

$watch()

Every time you bind something in the UI you insert a $watch in a $watch list.

User: <input type="text" ng-model="user" />
Password: <input type="password" ng-model="pass" />

Here we have $scope.user, which is bound to the first input, and we have $scope.pass, which is bound to the second one. Doing this we add two $watches to the $watch list.

When our template is loaded, AKA in the linking phase, the compiler will look for every directive and creates all the $watches that are needed.

AngularJS provides $watch, $watchcollection and $watch(true). Below is a neat diagram explaining all the three taken from watchers in depth.

Enter image description here

angular.module('MY_APP', []).controller('MyCtrl', MyCtrl)
function MyCtrl($scope,$timeout) {
  $scope.users = [{"name": "vinoth"},{"name":"yusuf"},{"name":"rajini"}];

  $scope.$watch("users", function() {
    console.log("**** reference checkers $watch ****")
  });

  $scope.$watchCollection("users", function() {
    console.log("**** Collection  checkers $watchCollection ****")
  });

  $scope.$watch("users", function() {
    console.log("**** equality checkers with $watch(true) ****")
  }, true);

  $timeout(function(){
     console.log("Triggers All ")
     $scope.users = [];
     $scope.$digest();

     console.log("Triggers $watchCollection and $watch(true)")
     $scope.users.push({ name: 'Thalaivar'});
     $scope.$digest();

     console.log("Triggers $watch(true)")
     $scope.users[0].name = 'Superstar';
     $scope.$digest();
  });
}

http://jsfiddle.net/2Lyn0Lkb/

$digest loop

When the browser receives an event that can be managed by the AngularJS context the $digest loop will be fired. This loop is made from two smaller loops. One processes the $evalAsync queue, and the other one processes the $watch list. The $digest will loop through the list of $watch that we have

app.controller('MainCtrl', function() {
  $scope.name = "vinoth";

  $scope.changeFoo = function() {
      $scope.name = "Thalaivar";
  }
});

{{ name }}
<button ng-click="changeFoo()">Change the name</button>

Here we have only one $watch because ng-click doesn’t create any watches.

We press the button.

  1. The browser receives an event which will enter the AngularJS context
  2. The $digest loop will run and will ask every $watch for changes.
  3. Since the $watch which was watching for changes in $scope.name reports a change, it will force another $digest loop.
  4. The new loop reports nothing.
  5. The browser gets the control back and it will update the DOM reflecting the new value of $scope.name
  6. The important thing here is that EVERY event that enters the AngularJS context will run a $digest loop. That means that every time we write a letter in an input, the loop will run checking every $watch in this page.

$apply()

If you call $apply when an event is fired, it will go through the angular-context, but if you don’t call it, it will run outside it. It is as easy as that. $apply will call the $digest() loop internally and it will iterate over all the watches to ensure the DOM is updated with the newly updated value.

The $apply() method will trigger watchers on the entire $scope chain whereas the $digest() method will only trigger watchers on the current $scope and its children. When none of the higher-up $scope objects need to know about the local changes, you can use $digest().