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

Commit

Permalink
feat(compiler): Precompute Scope.watch AST for TextMustache
Browse files Browse the repository at this point in the history
For #1086
  • Loading branch information
jbdeboer committed Jun 5, 2014
1 parent ecd75ce commit daf8d5a
Show file tree
Hide file tree
Showing 8 changed files with 44 additions and 24 deletions.
4 changes: 3 additions & 1 deletion lib/core_dom/common.dart
Expand Up @@ -19,15 +19,17 @@ class DirectiveRef {
final Key typeKey;
final Directive annotation;
final String value;
final AST valueAST;
final mappings = new List<MappingParts>();

DirectiveRef(this.element, this.type, this.annotation, this.typeKey, [ this.value ]);
DirectiveRef(this.element, this.type, this.annotation, this.typeKey, [ this.value, this.valueAST ]);

String toString() {
var html = element is dom.Element
? (element as dom.Element).outerHtml
: element.nodeValue;
return '{ element: $html, selector: ${annotation.selector}, value: $value, '
'ast: ${valueAST == null ? 'null' : '$valueAST'}, '
'type: $type }';
}
}
Expand Down
4 changes: 3 additions & 1 deletion lib/core_dom/directive_map.dart
Expand Up @@ -3,13 +3,15 @@ part of angular.core.dom_internal;
@Injectable()
class DirectiveMap extends AnnotationsMap<Directive> {
DirectiveSelectorFactory _directiveSelectorFactory;
FormatterMap _formatters;
DirectiveSelector _selector;
DirectiveSelector get selector {
if (_selector != null) return _selector;
return _selector = _directiveSelectorFactory.selector(this);
return _selector = _directiveSelectorFactory.selector(this, _formatters);
}

DirectiveMap(Injector injector,
this._formatters,
MetadataExtractor metadataExtractor,
this._directiveSelectorFactory)
: super(injector, metadataExtractor);
Expand Down
5 changes: 2 additions & 3 deletions lib/core_dom/element_binder.dart
Expand Up @@ -247,9 +247,8 @@ class ElementBinder {
void _createDirectiveFactories(DirectiveRef ref, nodeModule, node, nodesAttrsDirectives, nodeAttrs,
visibility) {
if (ref.type == TextMustache) {
nodeModule.bindByKey(TEXT_MUSTACHE_KEY, toFactory: (Injector injector) {
return new TextMustache(node, ref.value, injector.getByKey(INTERPOLATE_KEY),
injector.getByKey(SCOPE_KEY), injector.getByKey(FORMATTER_MAP_KEY));
nodeModule.bind(TextMustache, toFactory: (Injector injector) {
return new TextMustache(node, ref.valueAST, injector.getByKey(SCOPE_KEY));
});
} else if (ref.type == AttrMustache) {
if (nodesAttrsDirectives.isEmpty) {
Expand Down
1 change: 1 addition & 0 deletions lib/core_dom/module_internal.dart
Expand Up @@ -16,6 +16,7 @@ import 'package:angular/core_dom/dom_util.dart' as util;
import 'package:angular/core_dom/static_keys.dart';

import 'package:angular/change_detection/watch_group.dart' show Watch, PrototypeMap;
import 'package:angular/change_detection/ast_parser.dart';
import 'package:angular/core/registry.dart';

import 'package:angular/directive/module.dart' show NgBaseCss;
Expand Down
13 changes: 3 additions & 10 deletions lib/core_dom/mustache.dart
Expand Up @@ -5,17 +5,10 @@ part of angular.core.dom_internal;
class TextMustache {
final dom.Node _element;

TextMustache(this._element,
String template,
Interpolate interpolate,
Scope scope,
FormatterMap formatters) {
String expression = interpolate(template);

scope.watch(expression,
TextMustache(this._element, AST ast, Scope scope) {
scope.watchAST(ast,
_updateMarkup,
canChangeModel: false,
formatters: formatters);
canChangeModel: false);
}

void _updateMarkup(text, previousText) {
Expand Down
30 changes: 25 additions & 5 deletions lib/core_dom/selector.dart
Expand Up @@ -23,12 +23,15 @@ part of angular.core.dom_internal;
class DirectiveSelector {
ElementBinderFactory _binderFactory;
DirectiveMap _directives;
Interpolate _interpolate;
FormatterMap _formatters;
ASTParser _astParser;
var elementSelector = new _ElementSelector('');
var attrSelector = <_ContainsSelector>[];
var textSelector = <_ContainsSelector>[];

/// Parses all the [_directives] so they can be retrieved via [matchElement]
DirectiveSelector(this._directives, this._binderFactory) {
DirectiveSelector(this._directives, this._formatters, this._binderFactory, this._interpolate, this._astParser) {
_directives.forEach((Directive annotation, Type type) {
var match;
var selector = annotation.selector;
Expand Down Expand Up @@ -130,8 +133,12 @@ class DirectiveSelector {
var selectorRegExp = textSelector[k];
if (selectorRegExp.regexp.hasMatch(value)) {
_directives[selectorRegExp.annotation].forEach((type) {
// Pre-compute the AST to watch this value.
String expression = _interpolate(value);
var valueAST = _astParser(expression, formatters: _formatters);

builder.addDirective(new DirectiveRef(node, type,
selectorRegExp.annotation, new Key(type), value));
selectorRegExp.annotation, new Key(type), value, valueAST));
});
}
}
Expand All @@ -147,11 +154,24 @@ class DirectiveSelector {
@Injectable()
class DirectiveSelectorFactory {
ElementBinderFactory _binderFactory;
Interpolate _interpolate;
ASTParser _astParser;
// TODO(deboer): Remove once the FormatterMap is a required 'selector' parameter.
FormatterMap _defaultFormatterMap;

DirectiveSelectorFactory(this._binderFactory);
DirectiveSelectorFactory(this._binderFactory, this._interpolate,
this._astParser, this._defaultFormatterMap);

DirectiveSelector selector(DirectiveMap directives) =>
new DirectiveSelector(directives, _binderFactory);
/**
* Create a new [DirectiveSelector] given a [DirectiveMap] and [FormatterMap]
*
* NOTE: [formatters] will become required very soon. New code must pass
* both parameters.
*/
DirectiveSelector selector(DirectiveMap directives, [FormatterMap formatters]) =>
new DirectiveSelector(directives,
formatters != null ? formatters : _defaultFormatterMap,
_binderFactory, _interpolate, _astParser);
}

class _Directive {
Expand Down
7 changes: 5 additions & 2 deletions test/core_dom/selector_spec.dart
Expand Up @@ -209,7 +209,9 @@ main() {
it('should match text', () {
expect(selector(element = e('before-abc-after')),
toEqualsDirectiveInfos([
{ "selector": ':contains(/abc/)', "value": 'before-abc-after',
{ "selector": ':contains(/abc/)',
"value": 'before-abc-after',
"ast": '"before-abc-after"',
"element": element, "name": '#text'}
]));
});
Expand Down Expand Up @@ -249,7 +251,8 @@ class DirectiveInfosMatcher extends Matcher {
bool _refMatches(directiveRef, expectedMap) =>
directiveRef.element == expectedMap['element'] &&
directiveRef.annotation.selector == expectedMap['selector'] &&
directiveRef.value == expectedMap['value'];
directiveRef.value == expectedMap['value'] &&
(directiveRef.valueAST == null || directiveRef.valueAST.expression == expectedMap['ast']);


bool matches(ElementBinder binder, matchState) {
Expand Down
4 changes: 2 additions & 2 deletions test/core_dom/view_spec.dart
Expand Up @@ -212,7 +212,7 @@ main() {
compiler(es('<dir-a>{{\'a\' | formatterA}}</dir-a><dir-b></dir-b>'), directives)(rootInjector);
rootScope.apply();

expect(log.log, equals(['ADirective', 'AFormatter']));
expect(log.log, equals(['AFormatter', 'ADirective']));


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

expect(log.log, equals(['ADirective', 'AFormatter', 'ADirective', 'BDirective', 'BFormatter']));
expect(log.log, equals(['AFormatter', 'ADirective', 'BFormatter', 'ADirective', 'BDirective']));
});

});
Expand Down

0 comments on commit daf8d5a

Please sign in to comment.