2

I have inherited some AngularJS code, and I know very little about it (the code and Angular both). The code that I inherited contains several places where $scope.$digest is called within a $scope.$on method within a controller. For example:

$scope.$on("ScriptEditorEnabled", function (e, enabled) {
    $scope.scriptEditorDisabled = !enabled;
    $scope.$digest();
});

In my context, this is causing the popular $digest already in progress error (AngularJS : Prevent error $digest already in progress when calling $scope.$apply()). Isn't an $on method part of the $scope object, and thus, any changes to its data would be picked up automatically? If so, doesn't that mean that calling $digest within one (either directly or indirectly), just plain incorrect?

I've been simply removing these calls, seemingly without any loss in functionality, and the errors go away. Is there any dangers I should be aware of when doing this?

Community
  • 1
  • 1
MuertoExcobito
  • 9,741
  • 2
  • 37
  • 78

2 Answers2

4

$scope.$on gets called in response to $scope.$broadcast or $scope.$emit. The $scope.$on should always assume that it runs inside a digest loop. Neither $apply nor $digest calls should be needed inside the $on handler.

Angular is pretty clear about it in the When to use $scope.$apply(). If you dispatch events from non-angular environments, you should wrap the $emit or $broadcast call in $apply instead of calling it in the event handler. Example:

$rootScope.$apply($rootScope.$broadcast('recievedMsg', msg));

It's dispatcher responsibility to enter the digest loop if it knows there isn't one started already.

Alexis Cramatte
  • 408
  • 3
  • 6
fracz
  • 20,536
  • 18
  • 103
  • 149
1

Isn't an $on method part of the $scope object, and thus, any changes to its data would be picked up automatically?

No. $scope.$on callback is triggered by $scope.$emit or $scope.$broadcast. If you inspect source code of both of them, you'll see that they don't by itself trigger $digest loop that checks for changes. This means that callback on $scope.$on can be executed from within $digest loop as well is outside of it. A good approach is to have a convention which defines whether the $scope.$emit or $scope.$broadcast$ should be executed inside $digest loop or the callback within $scope.$on should be executed inside $scope.$apply. Usually, it's the code that triggers event ensures the digest loop by using $scope.$apply().

A solution could be to use $scope.$evalAsync instead of $scope.$apply to avoid digest already in progress when calling $scope.$apply() error.

Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488