It seems like you're having trouble with the AngularJS data refresh cycle.
AngularJS sets up most events to be able to update / synchronize values between views and controllers. An example of this is input
(I'm talking about the oninput
event) when you use ng-model
.
Of course, we could say that AngularJS calls a method of synchronizing the values every time these events are triggered.
In this case, I believe that these methods are $apply
or $digest
².
Speaking specifically of $digest
, this is for the purpose of calling all $watchers
¹ (both manually created and automatically created, as in the case of ng-model
or ng-if
) for each action done by the user. / p>
These guys are responsible for the magic of AngularJS, behind the synchronization of values between View and Controller.
The problem usually occurs when you try to call the $digest
or $apply
method in AngularJS, without calling them outside of this Angular loop. Because the Angular is automatically programmed to call them. When attempting to do this manually, this causes an error:
angular.module('app', [])
.controller('AppController', function ($scope) {
$scope.name = 'Wallace';
$scope.$digest();
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script><divng-app="app">
<div ng-controller="AppController"> Hello, {{ name }}</div>
</div>
The error that appears is this:
[$ rootScope: inprog]
What seems to indicate that the "digest is already running".
If you use $apply
the error will be exactly the same.
So when to use $digest
or $apply
?
Use these guys when you need to update the AngularJS values in an event that is not provided for the automatic synchronization of AngularJS.
For example, if you want to apply a logic in AngularJS that depends on the event onscroll
or a mousemove
, you would need to use $apply
or $digest
for which the changes are processed, since AngularJS does not use them internally.
See:
angular.module('app', [])
.controller('AppController', function ($scope) {
$scope.name = 'Wallace';
angular.element(window).on('mousemove', function (e) {
$scope.y = e.clientX;
$scope.$digest();
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script><divng-app="app">
<p>Me o mouse para aparecer a posição</p>
<div ng-controller="AppController"> A posição do mouse é, {{ y }}</div>
</div>
And the $evalAsync
?
On account of what I explain above is that you had to use $evalAsync
. Because, as AngularJs does not accept that two $digest
is executed at the same time, you must use methods that $digest
asynchronously to force AngularJS to execute these $ watchers checks only when all other automatic $digest
have already been processed.
Methods such as $applyAsync
and $evalAsync
make a small "delay" in calling $digest
so that the error described above does not occur.
See:
angular.module('app', [])
.controller('AppController', function ($scope) {
$scope.name = 'Wallace';
$scope.$evalAsync(); // Ou '$applyAsync'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script><divng-app="app">
<div ng-controller="AppController"> Hello, {{ name }}</div>
</div>
Notes
¹ - When you use ng-if
, ng-show
, ng-model
, ng-class
or ng-style
(and anything else that works "magically" in AngularJS), you are implicitly creating $watcher
. If you use the $scope.$watch('nome_da_variavel')
method you are manually creating this $watcher
. It is these watchers that AngularJS runs in each update cycle, where the $digest
method is called.
² - There is a small difference between $digest
and $apply
. I asked about this: Which is the difference between $ digest and $ apply?