![]()
AngularJS has a built-in digest cycle that automatically detects changes in the application and updates the DOM (View). However, in some cases, especially when dealing with asynchronous operations, third-party libraries, or event listeners, Angular may not detect these changes. This is where $scope.$apply() comes in.
1. What is $scope.$apply()?
$scope.$apply() is a method in AngularJS that manually triggers a digest cycle ($digest()). It ensures that changes made outside Angular’s automatic digest cycle are reflected in the view.
Syntax:
$scope.$apply(function() {
// Code that modifies the scope
});
| Parameter | Description |
|---|---|
function() | A function where you update scope variables manually. |
2. Why Use $scope.$apply()?
Use Cases:
When changes occur outside Angular’s scope (e.g., event listeners, third-party libraries).
When updating the UI after an AJAX request.
When dealing with setTimeout() or setInterval().
When working with WebSockets.
3. Example Without $scope.$apply() (Issue)
Angular won’t detect changes if they happen outside its digest cycle.
var app = angular.module('myApp', []);
app.controller('MyController', function($scope) {
document.getElementById('updateBtn').addEventListener('click', function() {
$scope.message = "Button Clicked!";
console.log($scope.message); // Changes in the scope variable
});
});
<div ng-controller="MyController">
<p>{{ message }}</p>
<button id="updateBtn">Click Me</button>
</div>
Problem:
- The button click updates
$scope.message, but Angular doesn’t detect the change. - The UI will not update until another digest cycle runs (e.g., interacting with another bound element).
4. Fixing with $scope.$apply()
Manually trigger the digest cycle after updating the scope variable.
var app = angular.module('myApp', []);
app.controller('MyController', function($scope) {
document.getElementById('updateBtn').addEventListener('click', function() {
$scope.$apply(function() {
$scope.message = "Button Clicked!";
});
});
});
Fix Explanation:
$scope.$apply()notifies Angular that the scope has changed.- The digest cycle runs, and the UI updates immediately.
5. Handling Asynchronous Code (setTimeout & setInterval)
If an operation runs outside Angular’s scope (like setTimeout()), you need $scope.$apply().
Example Without $scope.$apply() (Issue)
app.controller('MyController', function($scope) {
setTimeout(function() {
$scope.message = "Updated after 3 seconds!";
console.log($scope.message); // UI does NOT update
}, 3000);
});
Problem:
- Angular does not detect changes made inside
setTimeout(). - The UI will not reflect the new
message.
Solution With $scope.$apply()
app.controller('MyController', function($scope) {
setTimeout(function() {
$scope.$apply(function() {
$scope.message = "Updated after 3 seconds!";
});
}, 3000);
});
Fix Explanation:
$scope.$apply()runs the digest cycle after updatingmessage.- The UI immediately updates after
setTimeout().
6. Handling AJAX Calls with $scope.$apply()
When using raw XMLHttpRequest or jQuery AJAX, Angular won’t detect changes automatically.
Example Without $scope.$apply() (Issue)
app.controller('MyController', function($scope) {
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(data => {
$scope.todo = data;
console.log($scope.todo); // UI does NOT update
});
});
Problem:
- Since
fetch()runs outside Angular’s digest cycle, changes won’t reflect in the UI.
Solution With $scope.$apply()
app.controller('MyController', function($scope) {
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(data => {
$scope.$apply(function() {
$scope.todo = data;
});
});
});
Fix Explanation:
$scope.$apply()ensures Angular runs a digest cycle after setting$scope.todo.- The UI updates properly.
7. Alternative: Using $timeout Instead of $scope.$apply()
AngularJS provides $timeout, which automatically triggers the digest cycle, making $scope.$apply() unnecessary.
app.controller('MyController', function($scope, $timeout) {
$timeout(function() {
$scope.message = "Updated after 3 seconds!";
}, 3000);
});
Why Use $timeout Instead?
✔ $timeout is a safe alternative to setTimeout().
✔ It automatically runs a digest cycle without needing $scope.$apply().
8. Avoiding $scope.$apply() Inside $digest()
If a digest cycle is already running, calling $apply() will cause an error.
Example (Incorrect Usage)
$scope.$apply(function() {
console.log("Updating scope...");
$scope.counter++;
});
If Angular is already inside a digest cycle, you’ll get:
Error: $digest already in progress
Fix: Use $evalAsync() Instead
$scope.$evalAsync(function() {
$scope.counter++;
});
Why?
$evalAsync()waits until the current digest cycle is complete before executing.
