![]()
If your $watch function is not triggering when the model changes in AngularJS, it can be due to several reasons, such as scope hierarchy issues, asynchronous updates, or improper data binding. Below, I’ll explain the possible causes and solutions in detail.
Step 1: Understanding How $watch Works
The $watch() function in AngularJS monitors a scope variable and triggers a callback when the value changes.
Basic Example of $watch
$scope.name = 'Narendra';
$scope.$watch('name', function(newVal, oldVal) {
console.log('Name changed from', oldVal, 'to', newVal);
});
If $scope.name changes, the callback should execute. If it’s not triggering, there’s an issue.
Step 2: Common Issues and Fixes
1. Scope Hierarchy Issue
If $watch() is defined on $scope, but the model is in a different scope, AngularJS may not detect changes.
Example of a Scope Issue
<div ng-controller="ParentCtrl">
<input type="text" ng-model="name">
<div ng-controller="ChildCtrl">
<p>Child: {{ name }}</p>
</div>
</div>
app.controller('ParentCtrl', function($scope) {
$scope.name = "Narendra";
});
app.controller('ChildCtrl', function($scope) {
$scope.$watch('name', function(newVal) {
console.log('Watch triggered:', newVal);
});
});
Here, ChildCtrl gets a new child scope, so $watch('name') in ChildCtrl doesn’t track changes in ParentCtrl.
Solution: Use $scope.$parent
Change $watch in ChildCtrl to:
$scope.$parent.$watch('name', function(newVal) {
console.log('Watch triggered:', newVal);
});
or use $rootScope if the variable is global:
app.run(function($rootScope) {
$rootScope.globalName = "Narendra";
});
app.controller('ChildCtrl', function($scope, $rootScope) {
$rootScope.$watch('globalName', function(newVal) {
console.log('Watch triggered:', newVal);
});
});
2. Object or Array Changes Not Detected
If $watch() is monitoring an object/array, it won’t trigger unless the reference changes.
Example
$scope.user = { name: "Narendra" };
$scope.$watch('user', function(newVal) {
console.log('User changed:', newVal);
});
If you update the property like this:
$scope.user.name = "Krishna";
The $watch won’t trigger because the reference (user) hasn’t changed.
Solution: Use Deep Watching (true as the third parameter)
$scope.$watch('user', function(newVal) {
console.log('User changed:', newVal);
}, true);
or use $watchCollection() for arrays:
$scope.$watchCollection('user', function(newVal) {
console.log('User changed:', newVal);
});
3. Model Updated Asynchronously (Not in Angular Context)
If the model is updated inside a setTimeout, an event listener, or an external API call, AngularJS won’t automatically detect the change.
Example
setTimeout(function() {
$scope.name = "Krishna";
}, 2000);
Here, $watch() won’t trigger because the change happens outside Angular’s digest cycle.
Solution: Use $apply() or $timeout
setTimeout(function() {
$scope.$apply(function() {
$scope.name = "Krishna";
});
}, 2000);
or use Angular’s $timeout (which internally triggers $apply()):
$timeout(function() {
$scope.name = "Krishna";
}, 2000);
4. Using One-Time Binding (::) in the View
If a variable is one-time bound using ::, it won’t update in the UI.
Example
<h1>{{::name}}</h1>
Even if $scope.name changes, the view won’t update, and $watch() won’t trigger.
Solution: Remove ::
<h1>{{ name }}</h1>
5. $watch() Defined After the Model Update
If $watch() is added after the model is modified, it won’t track the previous changes.
Example
$scope.name = "Krishna"; // Change before watch
$scope.$watch('name', function(newVal) {
console.log('Watch triggered:', newVal);
});
The $watch() starts tracking after name is already changed, so it won’t detect this initial update.
Solution: Move $watch() Earlier
Define $watch() before modifying the variable:
$scope.$watch('name', function(newVal) {
console.log('Watch triggered:', newVal);
});
$scope.name = "Krishna"; // This change is now detected
6. Watching a Computed Property
If $watch() is tracking a computed value (from a function), it won’t trigger automatically.
Example
$scope.getName = function() {
return $scope.firstName + " " + $scope.lastName;
};
$scope.$watch('getName()', function(newVal) {
console.log('Computed name changed:', newVal);
});
This won’t work because $watch() expects a scope variable, not a function.
Solution: Use a Function Callback
$scope.$watch(function() {
return $scope.firstName + " " + $scope.lastName;
}, function(newVal) {
console.log('Computed name changed:', newVal);
});
7. Digest Cycle Not Running
If changes are made but AngularJS doesn’t know about them, $watch() won’t trigger.
Example
$scope.name = "Krishna"; // No digest cycle started
Solution: Manually Trigger Digest
If necessary, call $scope.$apply():
$scope.$apply(function() {
$scope.name = "Krishna";
});
Step 3: Debugging Steps
- Check Scope Hierarchy – Is
$watch()in the correct scope? - Use Deep Watch (
true) – If tracking an object or array. - Wrap in
$apply()or$timeout()– If changes happen outside Angular’s context. - Remove
::One-Time Binding – If used in the view. - Define
$watch()Before Model Updates – To catch changes from the beginning. - Watch Functions Properly – Use a function callback instead of
getName(). - Manually Trigger
$apply()– If digest cycle isn’t running.
