Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 21f9316

Browse files
rodyhaddadIgorMinar
authored andcommitted
feat(Scope): add $watchGroup method for observing a set of expressions
Given an array of expressions, if any one expression changes then the listener function fires with an arrays of old and new values. $scope.watchGroup([expression1, expression2, expression3], function(newVals, oldVals) { // newVals and oldVals are arrays of values corresponding to expression1..3 ... }); Port of dart-archive/angular.dart@a3c31ce
1 parent 8d0cb30 commit 21f9316

File tree

2 files changed

+122
-1
lines changed

2 files changed

+122
-1
lines changed

src/ng/rootScope.js

+53-1
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,58 @@ function $RootScopeProvider(){
354354
};
355355
},
356356

357+
/**
358+
* @ngdoc method
359+
* @name $rootScope.Scope#$watchGroup
360+
* @function
361+
*
362+
* @description
363+
* A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
364+
* If any one expression in the collection changes the `listener` is executed.
365+
*
366+
* - The items in the `watchCollection` array are observed via standard $watch operation and are examined on every
367+
* call to $digest() to see if any items changes.
368+
* - The `listener` is called whenever any expression in the `watchExpressions` array changes.
369+
*
370+
* @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
371+
* watched using {@link ng.$rootScope.Scope#$watch $watch()}
372+
*
373+
* @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
374+
* expression in `watchExpressions` changes
375+
* The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
376+
* those of `watchExpression`
377+
* and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
378+
* those of `watchExpression`
379+
* The `scope` refers to the current scope.
380+
*
381+
* @returns {function()} Returns a de-registration function for all listeners.
382+
*/
383+
$watchGroup: function(watchExpressions, listener) {
384+
var oldValues = new Array(watchExpressions.length);
385+
var newValues = new Array(watchExpressions.length);
386+
var deregisterFns = [];
387+
var changeCount = 0;
388+
var self = this;
389+
390+
forEach(watchExpressions, function (expr, i) {
391+
deregisterFns.push(self.$watch(expr, function (value, oldValue) {
392+
newValues[i] = value;
393+
oldValues[i] = oldValue;
394+
changeCount++;
395+
}));
396+
}, this);
397+
398+
deregisterFns.push(self.$watch(function () {return changeCount;}, function () {
399+
listener(newValues, oldValues, self);
400+
}));
401+
402+
return function deregisterWatchGroup() {
403+
forEach(deregisterFns, function (fn) {
404+
fn();
405+
});
406+
};
407+
},
408+
357409

358410
/**
359411
* @ngdoc method
@@ -756,7 +808,7 @@ function $RootScopeProvider(){
756808

757809
// prevent NPEs since these methods have references to properties we nulled out
758810
this.$destroy = this.$digest = this.$apply = noop;
759-
this.$on = this.$watch = function() { return noop; };
811+
this.$on = this.$watch = this.$watchGroup = function() { return noop; };
760812
},
761813

762814
/**

test/ng/rootScopeSpec.js

+69
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,75 @@ describe('Scope', function() {
777777
});
778778
});
779779

780+
describe('$watchGroup', function() {
781+
var scope;
782+
var log;
783+
784+
beforeEach(inject(function($rootScope, _log_) {
785+
scope = $rootScope.$new();
786+
log = _log_;
787+
}));
788+
789+
790+
it('should work for a group with just a single expression', function() {
791+
scope.$watchGroup(['a'], function(values, oldValues, s) {
792+
expect(s).toBe(scope);
793+
log(oldValues + ' >>> ' + values);
794+
});
795+
796+
scope.a = 'foo';
797+
scope.$digest();
798+
expect(log).toEqual('foo >>> foo');
799+
800+
log.reset();
801+
scope.$digest();
802+
expect(log).toEqual('');
803+
804+
scope.a = 'bar';
805+
scope.$digest();
806+
expect(log).toEqual('foo >>> bar');
807+
});
808+
809+
810+
it('should detect a change to any one expression in the group', function() {
811+
scope.$watchGroup(['a', 'b'], function(values, oldValues, s) {
812+
expect(s).toBe(scope);
813+
log(oldValues + ' >>> ' + values);
814+
});
815+
816+
scope.a = 'foo';
817+
scope.b = 'bar';
818+
scope.$digest();
819+
expect(log).toEqual('foo,bar >>> foo,bar');
820+
821+
log.reset();
822+
scope.$digest();
823+
expect(log).toEqual('');
824+
825+
scope.a = 'a';
826+
scope.$digest();
827+
expect(log).toEqual('foo,bar >>> a,bar');
828+
829+
log.reset();
830+
scope.a = 'A';
831+
scope.b = 'B';
832+
scope.$digest();
833+
expect(log).toEqual('a,bar >>> A,B');
834+
});
835+
836+
837+
it('should not call watch action fn when watchGroup was deregistered', function() {
838+
var deregister = scope.$watchGroup(['a', 'b'], function(values, oldValues) {
839+
log(oldValues + ' >>> ' + values);
840+
});
841+
842+
deregister();
843+
scope.a = 'xxx';
844+
scope.b = 'yyy';
845+
scope.$digest();
846+
expect(log).toEqual('');
847+
});
848+
});
780849

781850
describe('$destroy', function() {
782851
var first = null, middle = null, last = null, log = null;

0 commit comments

Comments
 (0)