From a3c31ce1dddb4423faa316cb144568f3fc28b1a9 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Thu, 21 Nov 2013 18:48:09 +0000 Subject: [PATCH] feat(scope): add $watchSet API Allows watching a set of expressions. If any one expression changes then the reaction function fires. --- lib/core/scope.dart | 41 +++++++++++++++++++++++ test/core/scope_spec.dart | 69 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/lib/core/scope.dart b/lib/core/scope.dart index 9bc9c733e..f68a29f57 100644 --- a/lib/core/scope.dart +++ b/lib/core/scope.dart @@ -185,6 +185,47 @@ class Scope implements Map { return () => _watchers.remove(watcher); } + /** + * A variant of [$watch] where it watches a collection of [watchExpressios]. If any + * one expression in the collection changes the [listener] is executed. + * + * * [watcherExpressions] - `List` + * * [Listener] - `(List newValues, List previousValues, Scope scope)` + */ + $watchSet(List watchExpressions, [Function listener, String watchStr]) { + if (watchExpressions.length == 0) return () => null; + + var lastValues = new List(watchExpressions.length); + var currentValues = new List(watchExpressions.length); + + if (watchExpressions.length == 1) { + // Special case size of one. + return $watch(watchExpressions[0], (value, oldValue, scope) { + currentValues[0] = value; + lastValues[0] = oldValue; + listener(currentValues, lastValues, scope); + }); + } + var deregesterFns = []; + var changeCount = 0; + for(var i = 0, ii = watchExpressions.length; i < ii; i++) { + deregesterFns.add($watch(watchExpressions[i], (value, oldValue, __) { + currentValues[i] = value; + lastValues[i] = oldValue; + changeCount++; + })); + } + deregesterFns.add($watch((s) => changeCount, (c, o, scope) { + listener(currentValues, lastValues, scope); + })); + return () { + for(var i = 0, ii = deregesterFns.length; i < ii; i++) { + deregesterFns[i](); + } + }; + } + + $watchCollection(obj, listener, [String expression, bool shallow=false]) { var oldValue; var newValue; diff --git a/test/core/scope_spec.dart b/test/core/scope_spec.dart index 56c214f64..473c3a1ee 100644 --- a/test/core/scope_spec.dart +++ b/test/core/scope_spec.dart @@ -404,6 +404,75 @@ main() { }); + describe(r'$watchSet', () { + var scope; + beforeEach(inject((Scope s) => scope = s)); + + it('should skip empty sets', () { + expect(scope.$watchSet([], null)()).toBe(null); + }); + + it('should treat set of 1 as direct watch', () { + var lastValues = ['foo']; + var log = ''; + var clean = scope.$watchSet(['a'], (values, oldValues, s) { + log += values.join(',') + ';'; + expect(s).toBe(scope); + expect(oldValues).toEqual(lastValues); + lastValues = new List.from(values); + }); + + scope.a = 'foo'; + scope.$digest(); + expect(log).toEqual('foo;'); + + scope.$digest(); + expect(log).toEqual('foo;'); + + scope.a = 'bar'; + scope.$digest(); + expect(log).toEqual('foo;bar;'); + + clean(); + scope.a = 'xxx'; + scope.$digest(); + expect(log).toEqual('foo;bar;'); + }); + + it('should detect a change to any one in a set', () { + var lastValues = ['foo', 'bar']; + var log = ''; + var clean = scope.$watchSet(['a', 'b'], (values, oldValues, s) { + log += values.join(',') + ';'; + expect(oldValues).toEqual(lastValues); + lastValues = new List.from(values); + }); + + scope.a = 'foo'; + scope.b = 'bar'; + scope.$digest(); + expect(log).toEqual('foo,bar;'); + + scope.$digest(); + expect(log).toEqual('foo,bar;'); + + scope.a = 'a'; + scope.$digest(); + expect(log).toEqual('foo,bar;a,bar;'); + + scope.a = 'A'; + scope.b = 'B'; + scope.$digest(); + expect(log).toEqual('foo,bar;a,bar;A,B;'); + + clean(); + scope.a = 'xxx'; + scope.$digest(); + expect(log).toEqual('foo,bar;a,bar;A,B;'); + }); + }); + + describe(r'$destroy', () { var first = null, middle = null, last = null, log = null;