Let's dive into the concepts of $watch(), $digest(), and $apply() with a practical example:
<div ng-controller="myController">
{{data.time}}
<br/>
<button ng-click="updateTime()">Update Time - Angular Click Event</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>
In this demonstration, we bind the $scope.data.time variable to an interpolation directive to display its value in the HTML page. This binding automatically creates an internal watch on the $scope.data.time variable.
The example includes two buttons. The first button triggers the $scope.updateTime() function through an ng-click event listener. Upon clicking this button, AngularJS invokes $scope.$digest() to update data bindings accordingly.
However, the second button features a regular JavaScript event listener within the controller function. Although both buttons essentially perform the same task, the absence of $scope.$digest() after executing the event listener for the second button results in the updated data not being displayed. In other words, clicking the second button updates the $scope.data.time variable but fails to reflect the changes visually.
To resolve this issue, one can include a $scope.$digest() call at the end of the button event listener as follows:
document.getElementById("updateTimeButton")
.addEventListener('click', function() {
console.log("Update time clicked");
$scope.data.time = new Date();
$scope.$digest();
});
An alternative approach involves utilizing the $apply() function within the button event listener:
document.getElementById("updateTimeButton")
.addEventListener('click', function() {
$scope.$apply(function() {
console.log("Update time clicked");
$scope.data.time = new Date();
});
});
Take note of how $scope.$apply() is invoked inside the button event listener and the update operation on $scope.data.time is encapsulated within the function passed to $apply(). Once the $apply() call completes, AngularJS internally triggers $digest() to ensure all data bindings are updated accordingly.