Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is considered a watcher in Angular?

What are considered "watchers" in Angular? Are watchers themselves the only type of watchers, or are other Angular constructs such as ngModel watchers as well?

Or am I missing the big picture? For example, are watchers what enable directives like ngModel to work?

update: Is there a way to tell when there exists watchers? In testing I want to know when to call scope.$digest()

like image 777
LazerSharks Avatar asked Aug 03 '15 17:08

LazerSharks


3 Answers

watchers is nothing but dirty checking, which keeps a track of the old value and new value

They are getting evaluated on each digest cycle. It can be combination of scope variable or any expression. Angular does collect all of this watchers on each digest cycle and mainatain it inside $$watchers array. you could look at how many watchers are there by doing console.log($scope.$watchers) inside your controller.

Markup

<body class="container" ng-controller="myCtrl">
  Hello {{test}}
  I'm test to show $$watchers {{test1}}
  <div ng-show="show">SHowiing this div using ng-show</div>
  <div ng-if="show">SHowiing this div using ng-show</div>
  Watcher is not compulsary that should scope variables {{'1'}}
</body>

Plunkr

Guess in above code how many watchers are there, as you can see there are 3 {{}} interpolation directive that are going to place in watchers array and then if you see in console $scope.$$watchers it will show 5 watchers.

How come there it shows 5 watchers. As we can only see 3, Actually we've used ng-show and ng-if directive which internally place $watch on the expression provided in its attribute value. & those expression gets evaluated on each digest cycle.

You could also create your custom watcher by using $watch(deep/simple watch) & $watchGroup

Also you could have watcher use $attrs.$observe, this does work same as the watch but the only special thing it does it, it works for the interpolation variable.

$attrs.$observe('test',function(value){
    alert('')
});

Most of angular directive internally uses watcher like ng-repeat, ng-show, ng-if, ng-include, ng-switch, ng-bind,interpolation directive {{}}, filters, etc. They put watch internally to manage two way binding thing.

like image 115
Pankaj Parkar Avatar answered Oct 11 '22 17:10

Pankaj Parkar


Watchers (if we take only the documentation you are based on) are angular mechanisms aiming to observe in a two-way binding style a variable or a function result during any Angular digest cycle; and no matter what the triggering event of the digest cycle would be.

I would call a "watcher" any Angular mechanism that are able to trigger some codes based on ANY event that could occur.

Typically, to create a Watcher, you should use: $scope.watch(...)

Just be aware that it's better to avoid watchers as long as we can.
Indeed, their callback would be triggered at EACH digest cycle to perform dirty checking; often impacting performance.

ng-model is not linked with the concept of watcher.
ng-model is just a way to bind some variable from view to controller.
They are two distinct concepts.

like image 45
Mik378 Avatar answered Oct 11 '22 17:10

Mik378


To illustrate how $watch(), $digest() and $apply() works, look at this example:

<div ng-controller="myController">
{{data.time}}

<br/>
<button ng-click="updateTime()">update time - ng-click</button>
<button id="updateTimeButton"  >update time</button>
</div>
<script>
var module = angular.module("myapp", []);
var myController1 = module.controller("myController", function($scope) {
    $scope.data = { time : new Date() };
    $scope.updateTime = function() {
        $scope.data.time = new Date();
    }
    document.getElementById("updateTimeButton")
            .addEventListener('click', function() {
        console.log("update time clicked");
        $scope.data.time = new Date();
    });
});
</script>

This example binds the $scope.data.time variable to an interpolation directive which merges the variable value into the HTML page. This binding creates a watch internally on the $scope.data.time variable.

The example also contains two buttons. The first button has an ng-click listener attached to it. When that button is clicked the $scope.updateTime() function is called, and after that AngularJS calls $scope.$digest() so that data bindings are updated.

The second button gets a standard JavaScript event listener attached to it from inside the controller function. When the second button is clicked that listener function is executed. As you can see, the listener functions for both buttons do almost the same, but when the second button's listener function is called, the data binding is not updated. That is because the $scope.$digest() is not called after the second button's event listener is executed. Thus, if you click the second button the time is updated in the $scope.data.time variable, but the new time is never displayed.

To fix that we can add a $scope.$digest() call to the last line of the button event listener, like this:

document.getElementById("updateTimeButton")
    .addEventListener('click', function() {
console.log("update time clicked");
$scope.data.time = new Date();
$scope.$digest();
});

Instead of calling $digest() inside the button listener function you could also have used the $apply() function like this:

document.getElementById("updateTimeButton")
    .addEventListener('click', function() {
$scope.$apply(function() {
    console.log("update time clicked");
    $scope.data.time = new Date();
});
});

Notice how the $scope.$apply() function is called from inside the button event listener, and how the update of the $scope.data.time variable is performed inside the function passed as parameter to the $apply() function. When the $apply() function call finishes AngularJS calls $digest() internally, so all data bindings are updated.

like image 38
Rahul Modi Avatar answered Oct 11 '22 18:10

Rahul Modi