How to $ watch multiple variable changes in angular

Like $scope.$watch several variables in Angular and call a callback when one of them has changed.

 $scope.name = ... $scope.age = ... $scope.$watch('???',function(){ //called when name or age changed }) 
+74
angularjs
May 24 '13 at 7:40
source share
5 answers



UPDATE

Angular now offers two methods $ watchGroup (since version 1.3) and $ watchCollection . They were mentioned by @blazemonger and @kargold.




This should work regardless of types and values:

 $scope.$watch('[age,name]', function () { ... }, true); 

In this case, you should set the third parameter to true.

The string concatenation of 'age + name' will fail in the following case:

 <button ng-init="age=42;name='foo'" ng-click="age=4;name='2foo'">click</button> 

Before the user clicks the button, the observed value will be 42foo ( 42 + foo ) and after pressing 42foo ( 4 + 2foo ). Thus, the clock function will not be called. Therefore, it is better to use an array expression if you cannot guarantee that such a case does not appear.

 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <link href="//cdn.jsdelivr.net/jasmine/1.3.1/jasmine.css" rel="stylesheet" /> <script src="//cdn.jsdelivr.net/jasmine/1.3.1/jasmine.js"></script> <script src="//cdn.jsdelivr.net/jasmine/1.3.1/jasmine-html.js"></script> <script src="http://code.angularjs.org/1.2.0-rc.2/angular.js"></script> <script src="http://code.angularjs.org/1.2.0-rc.2/angular-mocks.js"></script> <script> angular.module('demo', []).controller('MainCtrl', function ($scope) { $scope.firstWatchFunctionCounter = 0; $scope.secondWatchFunctionCounter = 0; $scope.$watch('[age, name]', function () { $scope.firstWatchFunctionCounter++; }, true); $scope.$watch('age + name', function () { $scope.secondWatchFunctionCounter++; }); }); describe('Demo module', function () { beforeEach(module('demo')); describe('MainCtrl', function () { it('watch function should increment a counter', inject(function ($controller, $rootScope) { var scope = $rootScope.$new(); scope.age = 42; scope.name = 'foo'; var ctrl = $controller('MainCtrl', { '$scope': scope }); scope.$digest(); expect(scope.firstWatchFunctionCounter).toBe(1); expect(scope.secondWatchFunctionCounter).toBe(1); scope.age = 4; scope.name = '2foo'; scope.$digest(); expect(scope.firstWatchFunctionCounter).toBe(2); expect(scope.secondWatchFunctionCounter).toBe(2); // This will fail! })); }); }); (function () { var jasmineEnv = jasmine.getEnv(); var htmlReporter = new jasmine.HtmlReporter(); jasmineEnv.addReporter(htmlReporter); jasmineEnv.specFilter = function (spec) { return htmlReporter.specFilter(spec); }; var currentWindowOnload = window.onload; window.onload = function() { if (currentWindowOnload) { currentWindowOnload(); } execJasmine(); }; function execJasmine() { jasmineEnv.execute(); } })(); </script> </head> <body></body> </html> 

http://plnkr.co/edit/2DwCOftQTltWFbEDiDlA?p=preview

PS:

As pointed out by @reblace, of course, you can access the values:

 $scope.$watch('[age,name]', function (newValue, oldValue) { var newAge = newValue[0]; var newName = newValue[1]; var oldAge = oldValue[0]; var oldName = oldValue[1]; }, true); 
+111
Oct 10 '13 at 10:45
source share
 $scope.$watch('age + name', function () { //called when name or age changed }); 
+43
May 24 '13 at 9:22
source share

Angular 1.3 provides $ watchGroup specifically for this purpose:

https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watchGroup

This is similar to the same end result as the standard $ watch for an array of expressions. I like it because it makes the intention clearer in the code.

+7
May 26 '14 at 15:11
source share

There are many ways to view multiple values:

 //angular 1.1.4 $scope.$watchCollection(['foo', 'bar'], function(newValues, oldValues){ // do what you want here }); 

or later

 //angular 1.3 $scope.$watchGroup(['foo', 'bar'], function(newValues, oldValues, scope) { //do what you want here }); 

Read the white paper for more info: https://docs.angularjs.org/api/ng/type/ $ rootScope.Scope

+6
Jun 17 '14 at 12:21
source share

No one mentioned the obvious:

 var myCallback = function() { console.log("name or age changed"); }; $scope.$watch("name", myCallback); $scope.$watch("age", myCallback); 

This may mean a little less polling. If you look at both name + age (for this) and name (elsewhere), I assume that Angular will look at name twice to see if it is dirty.

It might be more readable to use a callback by name rather than embed it. Especially if you can give it a better name than in my example.

And you can look at the values ​​differently if you need to:

 $scope.$watch("buyers", myCallback, true); $scope.$watchCollection("sellers", myCallback); 

$watchGroup nice if you can use it, but as far as I can tell, it does not allow you to watch the members of the group as with a collection or with equality of objects.

If you need old and new values ​​of both expressions inside the same callback function call, then perhaps some of the other proposed solutions are more convenient.

+5
Jun 28 '14 at 11:53 on
source share



All Articles