Skip to content

Commit a3143f3

Browse files
committed
feat(Scope): add $watchSet method for observing a set of expressions
It any one expression changes then the listener function fires Port of dart-archive/angular.dart@a3c31ce
1 parent 1b1413a commit a3143f3

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

src/ng/rootScope.js

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

357+
/**
358+
* @ngdoc method
359+
* @name $rootScope.Scope#$watchSet
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+
$watchSet: function (watchExpressions, listener) {
384+
if (watchExpressions.length === 0) return noop;
385+
386+
var oldValues = new Array(watchExpressions.length),
387+
newValues = new Array(watchExpressions.length);
388+
389+
if (watchExpressions.length === 1) {
390+
// Special case size of one
391+
return this.$watch(watchExpressions[0], function (value, oldValue, scope) {
392+
newValues[0] = value;
393+
oldValues[0] = oldValue;
394+
listener.call(this, newValues, oldValues, scope);
395+
});
396+
}
397+
var deregisterFns = [],
398+
changeCount = 0;
399+
400+
forEach(watchExpressions, function (expr, i) {
401+
deregisterFns.push(this.$watch(expr, function (value, oldValue) {
402+
newValues[i] = value;
403+
oldValues[i] = oldValue;
404+
changeCount++;
405+
}));
406+
}, this);
407+
408+
deregisterFns.push(this.$watch(function () {return changeCount;}, function (c, o, scope) {
409+
listener.call(this, newValues, oldValues, scope);
410+
}));
411+
412+
return function () {
413+
forEach(deregisterFns, function (fn) {
414+
fn();
415+
});
416+
};
417+
},
418+
357419

358420
/**
359421
* @ngdoc method

test/ng/rootScopeSpec.js

+70
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,76 @@ describe('Scope', function() {
733733
});
734734
});
735735

736+
describe('$watchSet', function() {
737+
var scope;
738+
beforeEach(inject(function($rootScope) {
739+
scope = $rootScope.$new();
740+
}));
741+
742+
it('should skip empty sets', function() {
743+
expect(scope.$watchSet([], null)()).toBeUndefined();
744+
});
745+
746+
it('should treat set of 1 as direct watch', function() {
747+
var lastValues = ['foo'];
748+
var log = '';
749+
var clean = scope.$watchSet(['a'], function(values, oldValues, s) {
750+
log += values.join(',') + ';';
751+
expect(s).toBe(scope);
752+
expect(oldValues).toEqual(lastValues);
753+
lastValues = values.slice();
754+
});
755+
756+
scope.a = 'foo';
757+
scope.$digest();
758+
expect(log).toEqual('foo;');
759+
760+
scope.$digest();
761+
expect(log).toEqual('foo;');
762+
763+
scope.a = 'bar';
764+
scope.$digest();
765+
expect(log).toEqual('foo;bar;');
766+
767+
clean();
768+
scope.a = 'xxx';
769+
scope.$digest();
770+
expect(log).toEqual('foo;bar;');
771+
});
772+
773+
it('should detect a change to any one in a set', function() {
774+
var lastValues = ['foo', 'bar'];
775+
var log = '';
776+
var clean = scope.$watchSet(['a', 'b'], function(values, oldValues, s) {
777+
log += values.join(',') + ';';
778+
expect(s).toBe(scope);
779+
expect(oldValues).toEqual(lastValues);
780+
lastValues = values.slice();
781+
});
782+
783+
scope.a = 'foo';
784+
scope.b = 'bar';
785+
scope.$digest();
786+
expect(log).toEqual('foo,bar;');
787+
788+
scope.$digest();
789+
expect(log).toEqual('foo,bar;');
790+
791+
scope.a = 'a';
792+
scope.$digest();
793+
expect(log).toEqual('foo,bar;a,bar;');
794+
795+
scope.a = 'A';
796+
scope.b = 'B';
797+
scope.$digest();
798+
expect(log).toEqual('foo,bar;a,bar;A,B;');
799+
800+
clean();
801+
scope.a = 'xxx';
802+
scope.$digest();
803+
expect(log).toEqual('foo,bar;a,bar;A,B;');
804+
});
805+
});
736806

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

0 commit comments

Comments
 (0)