The $apply()
method in AngularJS is used to manually trigger a digest cycle ($digest
) when changes occur outside AngularJS’s scope, ensuring the UI updates correctly.
By default, AngularJS automatically triggers the digest cycle when handling events like ng-click
or ng-model
. However, if changes happen outside of AngularJS (e.g., inside setTimeout
, jQuery, or external APIs), Angular doesn’t detect them. This is where $apply()
helps.
How $apply()
Works Internally
1️⃣ Executes a function passed to it (if provided).
2️⃣ Calls $rootScope.$digest()
, running the digest cycle for the entire app.
3️⃣ Detects changes and updates the View accordingly.
Example: $apply()
in Action
Scenario: Updating UI with setTimeout
AngularJS doesn’t detect changes inside setTimeout
. We must use $apply()
to inform AngularJS.
<div ng-app="myApp" ng-controller="MainController">
<p>Counter: {{ counter }}</p>
<button ng-click="increment()">Increment</button>
<button ng-click="delayedIncrement()">Delayed Increment</button>
</div>
var app = angular.module("myApp", []);
app.controller("MainController", function ($scope) {
$scope.counter = 0;
// Normal AngularJS Event (No Need for $apply)
$scope.increment = function () {
$scope.counter++;
};
// Using setTimeout (Requires $apply)
$scope.delayedIncrement = function () {
setTimeout(function () {
$scope.$apply(function () {
$scope.counter++; // Manually trigger digest cycle
});
}, 2000);
};
});
Explanation:
increment()
works fine because AngularJS automatically triggers the digest cycle.delayedIncrement()
requires$apply()
sincesetTimeout
is outside AngularJS’s control.
Without $apply()
, AngularJS won’t recognize the change, and the UI won’t update.
$apply()
vs $digest()
Method | Scope | Use Case |
---|---|---|
$apply() | Calls $digest() on $rootScope (entire app) | Use when changes occur outside AngularJS (e.g., setTimeout , jQuery, external APIs). |
$digest() | Runs only on the current scope and child scopes | Use when you want to trigger a limited digest cycle (avoid performance overhead). |
Example using $digest()
:
setTimeout(function () {
$scope.counter++;
$scope.$digest(); // Only updates the current scope
}, 2000);
This may cause “$digest already in progress” errors if called inside an Angular event.
Common Issues with $apply()
1️⃣ “Digest already in progress” Error
If $apply()
is used inside an event that already runs a digest cycle, it causes an error.
Incorrect:
$scope.increment = function () {
$scope.counter++;
$scope.$apply(); // Error: $digest already in progress
};
Solution: Remove $apply()
since AngularJS already runs the digest cycle:
$scope.increment = function () {
$scope.counter++; // No need for $apply
};
2️⃣ Performance Considerations
$apply()
triggers a global digest cycle, which can be expensive for large applications.- Prefer local
$digest()
where possible to limit scope updates. - Use one-time binding (
::
) when data is static to reduce digest checks:<p>Name: {{ ::user.name }}</p>
Key Takeaways
Use $apply()
when modifying $scope
outside AngularJS (e.g., in setTimeout
, jQuery, or external libraries).
Avoid calling $apply()
inside AngularJS event handlers (like ng-click
).
Use $digest()
for performance optimization when needed.