Skip to content

Commit 8ed5c0f

Browse files
committed
feat(compiler): Precompute Scope.watch AST for TextMustache
For dart-archive#1086
1 parent f6885af commit 8ed5c0f

8 files changed

+33
-24
lines changed

lib/core_dom/common.dart

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,17 @@ class DirectiveRef {
1919
final Key typeKey;
2020
final Directive annotation;
2121
final String value;
22+
final AST valueAST;
2223
final mappings = new List<MappingParts>();
2324

24-
DirectiveRef(this.element, this.type, this.annotation, this.typeKey, [ this.value ]);
25+
DirectiveRef(this.element, this.type, this.annotation, this.typeKey, [ this.value, this.valueAST ]);
2526

2627
String toString() {
2728
var html = element is dom.Element
2829
? (element as dom.Element).outerHtml
2930
: element.nodeValue;
3031
return '{ element: $html, selector: ${annotation.selector}, value: $value, '
32+
'ast: ${valueAST == null ? 'null' : valueAST.expression}, '
3133
'type: $type }';
3234
}
3335
}

lib/core_dom/directive_map.dart

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ part of angular.core.dom_internal;
33
@Injectable()
44
class DirectiveMap extends AnnotationsMap<Directive> {
55
DirectiveSelectorFactory _directiveSelectorFactory;
6+
FormatterMap _formatters;
67
DirectiveSelector _selector;
78
DirectiveSelector get selector {
89
if (_selector != null) return _selector;
9-
return _selector = _directiveSelectorFactory.selector(this);
10+
return _selector = _directiveSelectorFactory.selector(this, _formatters);
1011
}
1112

1213
DirectiveMap(Injector injector,
14+
FormatterMap this._formatters,
1315
MetadataExtractor metadataExtractor,
1416
this._directiveSelectorFactory)
1517
: super(injector, metadataExtractor);

lib/core_dom/element_binder.dart

+2-3
Original file line numberDiff line numberDiff line change
@@ -247,9 +247,8 @@ class ElementBinder {
247247
void _createDirectiveFactories(DirectiveRef ref, nodeModule, node, nodesAttrsDirectives, nodeAttrs,
248248
visibility) {
249249
if (ref.type == TextMustache) {
250-
nodeModule.bindByKey(_TEXT_MUSTACHE_KEY, toFactory: (Injector injector) {
251-
return new TextMustache(node, ref.value, injector.getByKey(_INTERPOLATE_KEY),
252-
injector.getByKey(_SCOPE_KEY), injector.getByKey(_FORMATTER_MAP_KEY));
250+
nodeModule.bind(TextMustache, toFactory: (Injector injector) {
251+
return new TextMustache(node, ref.valueAST, injector.getByKey(_SCOPE_KEY));
253252
});
254253
} else if (ref.type == AttrMustache) {
255254
if (nodesAttrsDirectives.isEmpty) {

lib/core_dom/module_internal.dart

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import 'package:angular/core/parser/parser.dart';
1515
import 'package:angular/core_dom/dom_util.dart' as util;
1616

1717
import 'package:angular/change_detection/watch_group.dart' show Watch, PrototypeMap;
18+
import 'package:angular/change_detection/ast_parser.dart';
1819
import 'package:angular/core/registry.dart';
1920

2021
import 'package:angular/directive/module.dart' show NgBaseCss;

lib/core_dom/mustache.dart

+3-10
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,10 @@ part of angular.core.dom_internal;
55
class TextMustache {
66
final dom.Node _element;
77

8-
TextMustache(this._element,
9-
String template,
10-
Interpolate interpolate,
11-
Scope scope,
12-
FormatterMap formatters) {
13-
String expression = interpolate(template);
14-
15-
scope.watch(expression,
8+
TextMustache(this._element, AST ast, Scope scope) {
9+
scope.watchAST(ast,
1610
_updateMarkup,
17-
canChangeModel: false,
18-
formatters: formatters);
11+
canChangeModel: false);
1912
}
2013

2114
void _updateMarkup(text, previousText) {

lib/core_dom/selector.dart

+14-5
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,15 @@ part of angular.core.dom_internal;
2323
class DirectiveSelector {
2424
ElementBinderFactory _binderFactory;
2525
DirectiveMap _directives;
26+
Interpolate _interpolate;
27+
FormatterMap _formatters;
28+
ASTParser _astParser;
2629
var elementSelector = new _ElementSelector('');
2730
var attrSelector = <_ContainsSelector>[];
2831
var textSelector = <_ContainsSelector>[];
2932

3033
/// Parses all the [_directives] so they can be retrieved via [matchElement]
31-
DirectiveSelector(this._directives, this._binderFactory) {
34+
DirectiveSelector(this._directives, this._formatters, this._binderFactory, this._interpolate, this._astParser) {
3235
_directives.forEach((Directive annotation, Type type) {
3336
var match;
3437
var selector = annotation.selector;
@@ -130,8 +133,12 @@ class DirectiveSelector {
130133
var selectorRegExp = textSelector[k];
131134
if (selectorRegExp.regexp.hasMatch(value)) {
132135
_directives[selectorRegExp.annotation].forEach((type) {
136+
// Pre-compute the AST to watch this value.
137+
String expression = _interpolate(value);
138+
var valueAST = _astParser(expression, formatters: _formatters);
139+
133140
builder.addDirective(new DirectiveRef(node, type,
134-
selectorRegExp.annotation, new Key(type), value));
141+
selectorRegExp.annotation, new Key(type), value, valueAST));
135142
});
136143
}
137144
}
@@ -147,11 +154,13 @@ class DirectiveSelector {
147154
@Injectable()
148155
class DirectiveSelectorFactory {
149156
ElementBinderFactory _binderFactory;
157+
Interpolate _interpolate;
158+
ASTParser _astParser;
150159

151-
DirectiveSelectorFactory(this._binderFactory);
160+
DirectiveSelectorFactory(this._binderFactory, this._interpolate, this._astParser);
152161

153-
DirectiveSelector selector(DirectiveMap directives) =>
154-
new DirectiveSelector(directives, _binderFactory);
162+
DirectiveSelector selector(DirectiveMap directives, FormatterMap formatters) =>
163+
new DirectiveSelector(directives, formatters, _binderFactory, _interpolate, _astParser);
155164
}
156165

157166
class _Directive {

test/core_dom/selector_spec.dart

+5-2
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,9 @@ main() {
209209
it('should match text', () {
210210
expect(selector(element = e('before-abc-after')),
211211
toEqualsDirectiveInfos([
212-
{ "selector": ':contains(/abc/)', "value": 'before-abc-after',
212+
{ "selector": ':contains(/abc/)',
213+
"value": 'before-abc-after',
214+
"ast": '"before-abc-after"',
213215
"element": element, "name": '#text'}
214216
]));
215217
});
@@ -249,7 +251,8 @@ class DirectiveInfosMatcher extends Matcher {
249251
bool _refMatches(directiveRef, expectedMap) =>
250252
directiveRef.element == expectedMap['element'] &&
251253
directiveRef.annotation.selector == expectedMap['selector'] &&
252-
directiveRef.value == expectedMap['value'];
254+
directiveRef.value == expectedMap['value'] &&
255+
(directiveRef.valueAST == null || directiveRef.valueAST.expression == expectedMap['ast']);
253256

254257

255258
bool matches(ElementBinder binder, matchState) {

test/core_dom/view_spec.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ main() {
212212
compiler(es('<dir-a>{{\'a\' | formatterA}}</dir-a><dir-b></dir-b>'), directives)(rootInjector);
213213
rootScope.apply();
214214

215-
expect(log.log, equals(['ADirective', 'AFormatter']));
215+
expect(log.log, equals(['AFormatter', 'ADirective']));
216216

217217

218218
Module childModule = new Module()
@@ -226,7 +226,7 @@ main() {
226226
'<dir-b probe="dirB"></dir-b>{{\'b\' | formatterB}}'), newDirectives)(childInjector);
227227
rootScope.apply();
228228

229-
expect(log.log, equals(['ADirective', 'AFormatter', 'ADirective', 'BDirective', 'BFormatter']));
229+
expect(log.log, equals(['AFormatter', 'ADirective', 'BFormatter', 'ADirective', 'BDirective']));
230230
});
231231

232232
});

0 commit comments

Comments
 (0)