Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit 84762b1

Browse files
committed
feat(scope): Experimental: Watch once, watch not null expressions
This is an experimental feature, and may not get into v1.0. Depend on with caution.
1 parent 443df96 commit 84762b1

File tree

2 files changed

+61
-9
lines changed

2 files changed

+61
-9
lines changed

lib/core/scope.dart

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -173,19 +173,43 @@ class Scope {
173173
* controller code you will most likely use [watch].
174174
*/
175175
Watch watch(expression, ReactionFn reactionFn, {context, FilterMap filters}) {
176-
assert(expression != null);
177-
AST ast = expression is AST
178-
? expression
179-
: rootScope._astParser(expression, context: context, filters: filters);
180-
return watchGroup.watch(ast, reactionFn);
176+
return _watch(watchGroup, expression, reactionFn, context, filters);
181177
}
182178

183179
Watch observe(expression, ReactionFn reactionFn, {context, FilterMap filters}) {
180+
return _watch(observeGroup, expression, reactionFn, context, filters);
181+
}
182+
183+
Watch _watch(WatchGroup group, expression, ReactionFn reactionFn,
184+
context, FilterMap filters) {
184185
assert(expression != null);
185-
AST ast = expression is AST
186-
? expression
187-
: rootScope._astParser(expression, context: context, filters: filters);
188-
return observeGroup.watch(ast, reactionFn);
186+
AST ast;
187+
Watch watch;
188+
ReactionFn fn = reactionFn;
189+
if (expression is AST) {
190+
ast = expression;
191+
} else if (expression is String) {
192+
if (expression.startsWith('::')) {
193+
expression = expression.substring(2);
194+
fn = (value, last) {
195+
if (value != null) {
196+
watch.remove();
197+
return reactionFn(value, last);
198+
}
199+
};
200+
} else if (expression.startsWith(':')) {
201+
expression = expression.substring(1);
202+
fn = (value, last) {
203+
if (value != null) {
204+
return reactionFn(value, last);
205+
}
206+
};
207+
}
208+
ast = rootScope._astParser(expression, context: context, filters: filters);
209+
} else {
210+
throw 'expressions must be String or AST got $expression.';
211+
}
212+
return watch = group.watch(ast, fn);
189213
}
190214

191215
dynamic eval(expression, [Map locals]) {

test/core/scope_spec.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,34 @@ main() => describe('scope', () {
984984
}));
985985
});
986986

987+
988+
describe('special binding modes', () {
989+
it('should bind one time', inject((RootScope rootScope, Logger log) {
990+
rootScope.watch('foo', (v, _) => log('foo:$v'));
991+
rootScope.watch(':foo', (v, _) => log(':foo:$v'));
992+
rootScope.watch('::foo', (v, _) => log('::foo:$v'));
993+
994+
rootScope.apply();
995+
expect(log).toEqual(['foo:null']);
996+
log.clear();
997+
998+
rootScope.context['foo'] = true;
999+
rootScope.apply();
1000+
expect(log).toEqual(['foo:true', ':foo:true', '::foo:true']);
1001+
log.clear();
1002+
1003+
rootScope.context['foo'] = 123;
1004+
rootScope.apply();
1005+
expect(log).toEqual(['foo:123', ':foo:123']);
1006+
log.clear();
1007+
1008+
rootScope.context['foo'] = null;
1009+
rootScope.apply();
1010+
expect(log).toEqual(['foo:null']);
1011+
log.clear();
1012+
}));
1013+
});
1014+
9871015

9881016
describe('runAsync', () {
9891017
it(r'should run callback before watch', inject((RootScope rootScope) {

0 commit comments

Comments
 (0)