AngularJS
Web Development
JavaScript
$scope.$watch
$scope.$apply

How do I use $scope.$watch and $scope.$apply in AngularJS?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In AngularJS, $scope.$watch and $scope.$apply both relate to the digest cycle, but they solve different problems. $watch reacts to value changes that Angular tracks, while $apply tells Angular to run a digest after code outside Angular changes the model.

Use $scope.$watch to Observe Changes

A watch registers a listener for a scope expression. When Angular runs a digest and sees the watched value change, it calls your listener with the new and old values.

javascript
1$scope.total = 0;
2$scope.items = [
3  { name: 'Apple', quantity: 10 },
4  { name: 'Banana', quantity: 5 }
5];
6
7$scope.$watch('items', function (newValue) {
8  let sum = 0;
9  newValue.forEach(function (item) {
10    sum += item.quantity;
11  });
12  $scope.total = sum;
13}, true);

The third argument true enables deep watching, which means Angular also checks nested object and array properties. That is useful, but it is more expensive than watching a simple scalar value.

A common rule is to watch the smallest expression that solves the problem instead of deep-watching large object graphs by default.

Use $scope.$apply for External Events

Angular automatically runs a digest for many built-in events such as ng-click, $http, and $timeout. But if a value changes inside a callback from plain DOM code or a third-party library, Angular may not know about it.

That is when $apply is useful.

javascript
1document.getElementById('externalButton').addEventListener('click', function () {
2  $scope.$apply(function () {
3    $scope.message = 'updated outside Angular';
4  });
5});

The callback passed to $apply updates the model, and Angular then runs a digest so the view reflects the new value.

Know When You Do Not Need $apply

A frequent beginner mistake is wrapping Angular-managed code in $apply unnecessarily. If the update already happens inside ng-click, $http, $timeout, or another Angular service, a digest is already coming.

javascript
$scope.increment = function () {
  $scope.count += 1;
};

No $apply is needed here because Angular already invoked the function.

Calling $apply during an active digest can trigger errors such as "$digest already in progress." If you are unsure whether Angular already owns the control flow, check the source of the callback before adding it.

Watchers Are Powerful but Not Free

Each watcher runs during digest. Large numbers of watchers can slow down older AngularJS applications, especially when combined with deep watches or expensive listener functions.

A few practical habits help:

  • watch narrow expressions instead of whole objects when possible
  • prefer event-driven updates when watching is unnecessary
  • deregister watchers you no longer need
javascript
1var unwatch = $scope.$watch('searchText', function (newValue, oldValue) {
2  if (newValue !== oldValue) {
3    console.log('search changed');
4  }
5});
6
7$scope.$on('$destroy', function () {
8  unwatch();
9});

That cleanup matters when controllers and directives are created and destroyed repeatedly.

$watchCollection and $watchGroup

AngularJS also provides useful variants. $watchCollection is a lighter option for arrays and objects when you care about shallow collection changes, and $watchGroup lets you monitor multiple expressions together. These are often better fits than one deep watch over a large object.

The more precisely your watch matches the actual change you care about, the less work Angular has to do per digest.

Common Pitfalls

Using deep watches on large objects can create unnecessary digest overhead.

Calling $apply inside an Angular-managed callback can trigger digest-in-progress errors.

Leaving watchers active after the scope is gone can waste memory and CPU in long-lived screens.

Summary

  • Use $scope.$watch to respond to model changes during Angular's digest cycle.
  • Use $scope.$apply only when code outside Angular changes the model.
  • Avoid unnecessary deep watches and deregister watchers when they are no longer needed.
  • Do not wrap Angular-managed callbacks in $apply unless you know a digest is missing.

Course illustration
Course illustration

All Rights Reserved.