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

Commit

Permalink
feat(Scope): add $watchGroup method for observing a set of expressions
Browse files Browse the repository at this point in the history
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
  • Loading branch information
rodyhaddad authored and IgorMinar committed Apr 21, 2014
1 parent 8d0cb30 commit 21f9316
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 1 deletion.
54 changes: 53 additions & 1 deletion src/ng/rootScope.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,58 @@ function $RootScopeProvider(){
};
},

/**
* @ngdoc method
* @name $rootScope.Scope#$watchGroup
* @function
*
* @description
* A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
* If any one expression in the collection changes the `listener` is executed.
*
* - The items in the `watchCollection` array are observed via standard $watch operation and are examined on every
* call to $digest() to see if any items changes.
* - The `listener` is called whenever any expression in the `watchExpressions` array changes.
*
* @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
* watched using {@link ng.$rootScope.Scope#$watch $watch()}
*
* @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
* expression in `watchExpressions` changes
* The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
* those of `watchExpression`
* and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
* those of `watchExpression`
* The `scope` refers to the current scope.
*
* @returns {function()} Returns a de-registration function for all listeners.
*/
$watchGroup: function(watchExpressions, listener) {
var oldValues = new Array(watchExpressions.length);
var newValues = new Array(watchExpressions.length);
var deregisterFns = [];
var changeCount = 0;
var self = this;

forEach(watchExpressions, function (expr, i) {
deregisterFns.push(self.$watch(expr, function (value, oldValue) {
newValues[i] = value;
oldValues[i] = oldValue;
changeCount++;
}));
}, this);

deregisterFns.push(self.$watch(function () {return changeCount;}, function () {
listener(newValues, oldValues, self);
}));

return function deregisterWatchGroup() {
forEach(deregisterFns, function (fn) {
fn();
});
};
},


/**
* @ngdoc method
Expand Down Expand Up @@ -756,7 +808,7 @@ function $RootScopeProvider(){

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

/**
Expand Down
69 changes: 69 additions & 0 deletions test/ng/rootScopeSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,75 @@ describe('Scope', function() {
});
});

describe('$watchGroup', function() {
var scope;
var log;

beforeEach(inject(function($rootScope, _log_) {
scope = $rootScope.$new();
log = _log_;
}));


it('should work for a group with just a single expression', function() {
scope.$watchGroup(['a'], function(values, oldValues, s) {
expect(s).toBe(scope);
log(oldValues + ' >>> ' + values);
});

scope.a = 'foo';
scope.$digest();
expect(log).toEqual('foo >>> foo');

log.reset();
scope.$digest();
expect(log).toEqual('');

scope.a = 'bar';
scope.$digest();
expect(log).toEqual('foo >>> bar');
});


it('should detect a change to any one expression in the group', function() {
scope.$watchGroup(['a', 'b'], function(values, oldValues, s) {
expect(s).toBe(scope);
log(oldValues + ' >>> ' + values);
});

scope.a = 'foo';
scope.b = 'bar';
scope.$digest();
expect(log).toEqual('foo,bar >>> foo,bar');

log.reset();
scope.$digest();
expect(log).toEqual('');

scope.a = 'a';
scope.$digest();
expect(log).toEqual('foo,bar >>> a,bar');

log.reset();
scope.a = 'A';
scope.b = 'B';
scope.$digest();
expect(log).toEqual('a,bar >>> A,B');
});


it('should not call watch action fn when watchGroup was deregistered', function() {
var deregister = scope.$watchGroup(['a', 'b'], function(values, oldValues) {
log(oldValues + ' >>> ' + values);
});

deregister();
scope.a = 'xxx';
scope.b = 'yyy';
scope.$digest();
expect(log).toEqual('');
});
});

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

0 comments on commit 21f9316

Please sign in to comment.