1

I am an angular-noob and I am trying to understand why the following triggers the "$apply is already in progress ..." error.

Here is the code (and its really almost straight from the documentation: https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest)

<!DOCTYPE html>
<html>

<head>
    <script src="js/lib/angular.js"></script>
    <script>
    var app = angular.module('app', []);
    app.controller('MainController', ['$scope', function(scope) {
        scope.name = 'misko';
        scope.counter = 0;
        //A
        scope.$watch('name', function(newValue, oldValue) {
            scope.count++;
        });
        //B
        scope.$digest();
    }]);
    </script>
</head>

<body>
    <div ng-app='app' ng-controller="MainController">
    </div>
</body>

</html>

And, here is the fiddle: https://jsfiddle.net/zLny2faq/5/

I get the in-famous "$apply already in progress" error but why is that ?

I would like to know why the digest cycle triggers without even being called ?

In other words, I am merely declaring watch listeners (@comment A) and triggering them by calling $digest() (@comment B) . But it seems like its somehow being called sooner and errors out when I explicitly called $digest(). Why is this so ?

Please advise.

georgeawg
  • 48,608
  • 13
  • 72
  • 95
trk
  • 2,106
  • 14
  • 20
  • Your code doesn't make much sense. Why are you trying to call scope.$digest()? The $watch is already triggered because you set the name variable. In general you only need to call $digest/$apply outside the angular framework, e.g. in unit testing. – dz210 Mar 09 '16 at 18:56
  • @dz210 this code is almost straight from the documentation really .. but isn't the name variable really being set before $watch. – trk Mar 09 '16 at 19:02

2 Answers2

2

The name variable is set before the watch yes, but all watches get called once on initialization:

Per Angular Docs

After a watcher is registered with the scope, the listener fn is called asynchronously (via $evalAsync) to initialize the watcher. In rare cases, this is undesirable because the listener is called when the result of watchExpression didn't change. To detect this scenario within the listener fn, you can compare the newVal and oldVal. If these two values are identical (===) then the listener was called due to initialization.

^ Can be taken care of via:

$scope.$watch('name', function(newValue, oldValue) {
  if (newValue !== oldValue) {
    // will only run if the value changes, on initial load the newvalue = oldvalue so it won't run this code.
  }
});
Community
  • 1
  • 1
KreepN
  • 8,528
  • 1
  • 40
  • 58
1

If you really need to call $digest, you can use

$timeout(function(){
    scope.$digest();
}, 0);

Fiddle

alexey
  • 1,381
  • 9
  • 19