From a9ba4a3323d9e6506aa58599b3e99fcfa1a20728 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 4 Apr 2014 16:37:24 +0200 Subject: [PATCH 1/5] style: code cleanup --- lib/core/annotation.dart | 1 - lib/core/annotation_src.dart | 139 +++++++++++----------- lib/core/registry.dart | 12 +- lib/core/scope.dart | 2 +- lib/core_dom/common.dart | 10 +- lib/core_dom/directive_map.dart | 8 +- lib/core_dom/element_binder.dart | 37 +++--- lib/core_dom/element_binder_builder.dart | 4 +- lib/core_dom/module_internal.dart | 1 + lib/core_dom/node_cursor.dart | 7 +- lib/core_dom/selector.dart | 74 +++++------- lib/core_dom/tagging_compiler.dart | 32 ++--- lib/core_dom/tagging_view_factory.dart | 31 ++--- lib/core_dom/view.dart | 6 +- lib/core_dom/view_factory.dart | 95 +-------------- lib/core_dom/walking_view_factory.dart | 83 +++++++++++++ lib/directive/module.dart | 3 +- test/core_dom/selector_spec.dart | 141 ++++++++++++----------- 18 files changed, 335 insertions(+), 351 deletions(-) create mode 100644 lib/core_dom/walking_view_factory.dart diff --git a/lib/core/annotation.dart b/lib/core/annotation.dart index a869ec7c5..8f07daaf0 100644 --- a/lib/core/annotation.dart +++ b/lib/core/annotation.dart @@ -35,4 +35,3 @@ export "package:angular/core/annotation_src.dart" show abstract class NgShadowRootAware { void onShadowRoot(ShadowRoot shadowRoot); } - diff --git a/lib/core/annotation_src.dart b/lib/core/annotation_src.dart index c1f51d9cc..42c8387c1 100644 --- a/lib/core/annotation_src.dart +++ b/lib/core/annotation_src.dart @@ -6,19 +6,20 @@ RegExp _ATTR_NAME = new RegExp(r'\[([^\]]+)\]$'); const String SHADOW_DOM_INJECTOR_NAME = 'SHADOW_INJECTOR'; -skipShadow(Injector injector) - => injector.name == SHADOW_DOM_INJECTOR_NAME ? injector.parent : injector; +skipShadow(Injector injector) => + injector.name == SHADOW_DOM_INJECTOR_NAME ? injector.parent : injector; -localVisibility (Injector requesting, Injector defining) - => identical(skipShadow(requesting), defining); +localVisibility (Injector requesting, Injector defining) => + identical(skipShadow(requesting), defining); directChildrenVisibility(Injector requesting, Injector defining) { requesting = skipShadow(requesting); - return identical(requesting.parent, defining) || localVisibility(requesting, defining); + return identical(requesting.parent, defining) || + localVisibility(requesting, defining); } -AbstractNgAnnotation cloneWithNewMap(AbstractNgAnnotation annotation, map) - => annotation._cloneWithNewMap(map); +AbstractNgAnnotation cloneWithNewMap(AbstractNgAnnotation annotation, map) => + annotation._cloneWithNewMap(map); String mappingSpec(AbstractNgFieldAnnotation annotation) => annotation._mappingSpec; @@ -295,48 +296,46 @@ class NgComponent extends AbstractNgAnnotation { @deprecated final String publishAs; - const NgComponent({ - this.template, - this.templateUrl, - cssUrl, - applyAuthorStyles, - resetStyleInheritance, - this.publishAs, - module, - map, - selector, - visibility, - exportExpressions, - exportExpressionAttrs}) + const NgComponent({this.template, + this.templateUrl, + cssUrl, + applyAuthorStyles, + resetStyleInheritance, + this.publishAs, + module, + map, + selector, + visibility, + exportExpressions, + exportExpressionAttrs}) : _cssUrls = cssUrl, _applyAuthorStyles = applyAuthorStyles, _resetStyleInheritance = resetStyleInheritance, super(selector: selector, - children: AbstractNgAnnotation.COMPILE_CHILDREN, - visibility: visibility, - map: map, - module: module, - exportExpressions: exportExpressions, - exportExpressionAttrs: exportExpressionAttrs); + children: AbstractNgAnnotation.COMPILE_CHILDREN, + visibility: visibility, + map: map, + module: module, + exportExpressions: exportExpressions, + exportExpressionAttrs: exportExpressionAttrs); List get cssUrls => _cssUrls == null ? const [] : _cssUrls is List ? _cssUrls : [_cssUrls]; AbstractNgAnnotation _cloneWithNewMap(newMap) => - new NgComponent( - template: template, - templateUrl: templateUrl, - cssUrl: cssUrls, - applyAuthorStyles: applyAuthorStyles, - resetStyleInheritance: resetStyleInheritance, - publishAs: publishAs, - map: newMap, - module: module, - selector: selector, - visibility: visibility, - exportExpressions: exportExpressions, - exportExpressionAttrs: exportExpressionAttrs); + new NgComponent(template: template, + templateUrl: templateUrl, + cssUrl: cssUrls, + applyAuthorStyles: applyAuthorStyles, + resetStyleInheritance: resetStyleInheritance, + publishAs: publishAs, + map: newMap, + module: module, + selector: selector, + visibility: visibility, + exportExpressions: exportExpressions, + exportExpressionAttrs: exportExpressionAttrs); } /** @@ -367,12 +366,12 @@ class NgDirective extends AbstractNgAnnotation { static const Visibility DIRECT_CHILDREN_VISIBILITY = directChildrenVisibility; const NgDirective({children: AbstractNgAnnotation.COMPILE_CHILDREN, - map, - selector, - module, - visibility, - exportExpressions, - exportExpressionAttrs}) + map, + selector, + module, + visibility, + exportExpressions, + exportExpressionAttrs}) : super(selector: selector, children: children, visibility: visibility, @@ -382,14 +381,13 @@ class NgDirective extends AbstractNgAnnotation { exportExpressionAttrs: exportExpressionAttrs); AbstractNgAnnotation _cloneWithNewMap(newMap) => - new NgDirective( - children: children, - map: newMap, - module: module, - selector: selector, - visibility: visibility, - exportExpressions: exportExpressions, - exportExpressionAttrs: exportExpressionAttrs); + new NgDirective(children: children, + map: newMap, + module: module, + selector: selector, + visibility: visibility, + exportExpressions: exportExpressions, + exportExpressionAttrs: exportExpressionAttrs); } /** @@ -417,16 +415,14 @@ class NgController extends NgDirective { */ final String publishAs; - const NgController({ - children: AbstractNgAnnotation.COMPILE_CHILDREN, - this.publishAs, - map, - module, - selector, - visibility, - exportExpressions, - exportExpressionAttrs - }) + const NgController({children: AbstractNgAnnotation.COMPILE_CHILDREN, + this.publishAs, + map, + module, + selector, + visibility, + exportExpressions, + exportExpressionAttrs}) : super(selector: selector, children: children, visibility: visibility, @@ -436,15 +432,14 @@ class NgController extends NgDirective { exportExpressionAttrs: exportExpressionAttrs); AbstractNgAnnotation _cloneWithNewMap(newMap) => - new NgController( - children: children, - publishAs: publishAs, - module: module, - map: newMap, - selector: selector, - visibility: visibility, - exportExpressions: exportExpressions, - exportExpressionAttrs: exportExpressionAttrs); + new NgController(children: children, + publishAs: publishAs, + module: module, + map: newMap, + selector: selector, + visibility: visibility, + exportExpressions: exportExpressions, + exportExpressionAttrs: exportExpressionAttrs); } /** diff --git a/lib/core/registry.dart b/lib/core/registry.dart index 9d66de3ed..c9148855a 100644 --- a/lib/core/registry.dart +++ b/lib/core/registry.dart @@ -42,20 +42,20 @@ abstract class AnnotationsMap { extractMetadata(type) .where((annotation) => annotation is K) .forEach((annotation) { - map.putIfAbsent(annotation, () => []).add(type); + map.putIfAbsent(annotation, () => []).add(type); }); }); } - List operator[](K annotation) { + List operator[](K annotation) { var value = map[annotation]; if (value == null) throw 'No $annotation found!'; return value; } void forEach(fn(K, Type)) { - map.forEach((annotation, types) { - types.forEach((type) { + map.forEach((K annotation, List types) { + types.forEach((Type type) { fn(annotation, type); }); }); @@ -63,8 +63,8 @@ abstract class AnnotationsMap { List annotationsFor(Type type) { var res = []; - forEach((ann, annType) { - if (annType == type) res.add(ann); + map.forEach((K ann, List types) { + if (types.contains(type)) res.add(ann); }); return res; } diff --git a/lib/core/scope.dart b/lib/core/scope.dart index 5736adcd7..966ebd314 100644 --- a/lib/core/scope.dart +++ b/lib/core/scope.dart @@ -219,7 +219,7 @@ class Scope { } else if (expression.startsWith(':')) { expression = expression.substring(1); fn = (value, last) { - if (value != null) reactionFn(value, last); + if (value != null) reactionFn(value, last); }; } } diff --git a/lib/core_dom/common.dart b/lib/core_dom/common.dart index 98e5905a8..24874c5c2 100644 --- a/lib/core_dom/common.dart +++ b/lib/core_dom/common.dart @@ -1,8 +1,6 @@ part of angular.core.dom_internal; -List cloneElements(elements) { - return elements.map((el) => el.clone(true)).toList(); -} +List cloneElements(els) => els.map((el) => el.clone(true)).toList(); class MappingParts { final String attrName; @@ -18,7 +16,7 @@ class DirectiveRef { final Type type; final AbstractNgAnnotation annotation; final String value; - final mappings = new List(); + final mappings = []; DirectiveRef(this.element, this.type, this.annotation, [ this.value ]); @@ -37,8 +35,8 @@ class DirectiveRef { */ Injector forceNewDirectivesAndFilters(Injector injector, List modules) { modules.add(new Module() - ..factory(Scope, (i) { - var scope = i.parent.get(Scope); + ..factory(Scope, (inj) { + var scope = inj.parent.get(Scope); return scope.createChild(new PrototypeMap(scope.context)); })); diff --git a/lib/core_dom/directive_map.dart b/lib/core_dom/directive_map.dart index c093d9df9..e00a59fae 100644 --- a/lib/core_dom/directive_map.dart +++ b/lib/core_dom/directive_map.dart @@ -2,11 +2,13 @@ part of angular.core.dom_internal; @NgInjectableService() class DirectiveMap extends AnnotationsMap { - DirectiveSelectorFactory _directiveSelectorFactory; + final DirectiveSelectorFactory _directiveSelectorFactory; DirectiveSelector _selector; DirectiveSelector get selector { - if (_selector != null) return _selector; - return _selector = _directiveSelectorFactory.selector(this); + if (_selector == null) { + _selector = _directiveSelectorFactory.selector(this); + } + return _selector; } DirectiveMap(Injector injector, diff --git a/lib/core_dom/element_binder.dart b/lib/core_dom/element_binder.dart index b246231b6..fda4b5724 100644 --- a/lib/core_dom/element_binder.dart +++ b/lib/core_dom/element_binder.dart @@ -22,12 +22,11 @@ class TemplateElementBinder extends ElementBinder { _registerViewFactory(node, parentInjector, nodeModule) { assert(templateViewFactory != null); - nodeModule - ..factory(ViewPort, (_) => - new ViewPort(node, parentInjector.get(NgAnimate))) - ..value(ViewFactory, templateViewFactory) - ..factory(BoundViewFactory, (Injector injector) => - templateViewFactory.bind(injector)); + nodeModule..factory(ViewPort, (_) => + new ViewPort(node, parentInjector.get(NgAnimate))) + ..value(ViewFactory, templateViewFactory) + ..factory(BoundViewFactory, (Injector injector) => + templateViewFactory.bind(injector)); } } @@ -61,10 +60,14 @@ class ElementBinder { childMode == AbstractNgAnnotation.COMPILE_CHILDREN; var _directiveCache; + List get _usableDirectiveRefs { - if (_directiveCache != null) return _directiveCache; - if (component != null) return _directiveCache = new List.from(decorators)..add(component); - return _directiveCache = decorators; + if (_directiveCache == null) { + _directiveCache = component != null ? + (new List.from(decorators)..add(component)): + decorators; + } + return _directiveCache; } bool get hasDirectivesOrEvents => @@ -151,7 +154,7 @@ class ElementBinder { }); } - _link(nodeInjector, probe, scope, nodeAttrs, filters) { + void _link(nodeInjector, probe, scope, nodeAttrs, filters) { _usableDirectiveRefs.forEach((DirectiveRef ref) { var linkTimer; try { @@ -174,11 +177,11 @@ class ElementBinder { if (controller is NgAttachAware) { var taskId = tasks.registerTask(); Watch watch; - watch = scope.watch('1', // Cheat a bit. + watch = scope.watch( + '::1', // Cheat a bit. (_, __) { - watch.remove(); - tasks.completeTask(taskId); - }); + tasks.completeTask(taskId); + }); } tasks.doneRegistering(); @@ -194,7 +197,7 @@ class ElementBinder { }); } - _createDirectiveFactories(DirectiveRef ref, nodeModule, node, nodesAttrsDirectives, nodeAttrs, + void _createDirectiveFactories(DirectiveRef ref, nodeModule, node, nodesAttrsDirectives, nodeAttrs, visibility) { if (ref.type == NgTextMustacheDirective) { nodeModule.factory(NgTextMustacheDirective, (Injector injector) { @@ -240,7 +243,7 @@ class ElementBinder { } // Overridden in TemplateElementBinder - _registerViewFactory(node, parentInjector, nodeModule) { + void _registerViewFactory(node, parentInjector, nodeModule) { nodeModule..factory(ViewPort, null) ..factory(ViewFactory, null) ..factory(BoundViewFactory, null); @@ -355,7 +358,7 @@ class TaggedTextBinder { final int offsetIndex; TaggedTextBinder(this.binder, this.offsetIndex); - toString() => "[TaggedTextBinder binder:$binder offset:$offsetIndex]"; + String toString() => "[TaggedTextBinder binder:$binder offset:$offsetIndex]"; } // Used for the tagging compiler diff --git a/lib/core_dom/element_binder_builder.dart b/lib/core_dom/element_binder_builder.dart index c74f7c1a7..d81ad7005 100644 --- a/lib/core_dom/element_binder_builder.dart +++ b/lib/core_dom/element_binder_builder.dart @@ -24,7 +24,7 @@ class ElementBinderFactory { * building ElementBinders. */ class ElementBinderBuilder { - static RegExp _MAPPING = new RegExp(r'^(\@|=\>\!|\=\>|\<\=\>|\&)\s*(.*)$'); + static RegExp _MAPPING = new RegExp(r'^(@|=>!|=>|<=>|&)\s*(.*)$'); ElementBinderFactory _factory; @@ -76,10 +76,10 @@ class ElementBinderBuilder { if (template != null) { var transclude = _factory.binder(this); return _factory.templateBinder(this, transclude); - } else { return _factory.binder(this); } + return mappingFn; } } diff --git a/lib/core_dom/module_internal.dart b/lib/core_dom/module_internal.dart index b527d8a8a..e3a0f3628 100644 --- a/lib/core_dom/module_internal.dart +++ b/lib/core_dom/module_internal.dart @@ -38,6 +38,7 @@ part 'tagging_view_factory.dart'; part 'template_cache.dart'; part 'tree_sanitizer.dart'; part 'walking_compiler.dart'; +part 'walking_view_factory.dart'; part 'ng_element.dart'; class NgCoreDomModule extends Module { diff --git a/lib/core_dom/node_cursor.dart b/lib/core_dom/node_cursor.dart index a88fe2dfe..85f40dc77 100644 --- a/lib/core_dom/node_cursor.dart +++ b/lib/core_dom/node_cursor.dart @@ -12,12 +12,11 @@ class NodeCursor { dom.Node get current => index < elements.length ? elements[index] : null; bool descend() { - var childNodes = elements[index].nodes; - var hasChildren = childNodes != null && childNodes.isNotEmpty; + var hasChildren = elements[index].hasChildNodes(); if (hasChildren) { stack..add(index)..add(elements); - elements = new List.from(childNodes); + elements = new List.from(elements[index].nodes); index = 0; } @@ -45,5 +44,5 @@ class NodeCursor { NodeCursor remove() => new NodeCursor([elements.removeAt(index)..remove()]); - toString() => "[NodeCursor: $elements $index]"; + String toString() => "[NodeCursor: $elements $index]"; } diff --git a/lib/core_dom/selector.dart b/lib/core_dom/selector.dart index 7f7cbed7a..9b290ca0b 100644 --- a/lib/core_dom/selector.dart +++ b/lib/core_dom/selector.dart @@ -16,15 +16,12 @@ part of angular.core.dom_internal; * * Examples: * - *
- *   element
- *   .class
- *   [attribute]
- *   [attribute=value]
- *   element[attribute1][attribute2=value]
- *   :contains(/abc/)
- * 
- * + * * `element` + * * `.class` + * * `[attribute]` + * * `[attribute=value]` + * * `element[attribute1][attribute2=value]` + * * `:contains(/abc/)` * */ class _Directive { @@ -33,7 +30,7 @@ class _Directive { _Directive(this.type, this.annotation); - toString() => annotation.selector; + String toString() => annotation.selector; } @@ -45,11 +42,11 @@ class _ContainsSelector { : regexp = new RegExp(regexp); } -var _SELECTOR_REGEXP = new RegExp(r'^(?:([-\w]+)|(?:\.([-\w]+))|' +final _SELECTOR_REGEXP = new RegExp(r'^(?:([-\w]+)|(?:\.([-\w]+))|' r'(?:\[([-\w*]+)(?:=([^\]]*))?\]))'); -var _COMMENT_COMPONENT_REGEXP = new RegExp(r'^\[([-\w]+)(?:\=(.*))?\]$'); -var _CONTAINS_REGEXP = new RegExp(r'^:contains\(\/(.+)\/\)$'); // -var _ATTR_CONTAINS_REGEXP = new RegExp(r'^\[\*=\/(.+)\/\]$'); // +final _COMMENT_COMPONENT_REGEXP = new RegExp(r'^\[([-\w]+)(?:\=(.*))?\]$'); +final _CONTAINS_REGEXP = new RegExp(r'^:contains\(\/(.+)\/\)$'); // +final _ATTR_CONTAINS_REGEXP = new RegExp(r'^\[\*=\/(.+)\/\]$'); // class _SelectorPart { final String element; @@ -95,7 +92,7 @@ class _ElementSelector { _ElementSelector(this.name); - addDirective(List<_SelectorPart> selectorParts, _Directive directive) { + void addDirective(List<_SelectorPart> selectorParts, _Directive directive) { var selectorPart = selectorParts.removeAt(0); var terminal = selectorParts.isEmpty; var name; @@ -144,9 +141,7 @@ class _ElementSelector { _addRefs(builder, elementMap[nodeName], node); } if (elementPartialMap.containsKey(nodeName)) { - if (partialSelection == null) { - partialSelection = new List<_ElementSelector>(); - } + if (partialSelection == null) partialSelection = <_ElementSelector>[]; partialSelection.add(elementPartialMap[nodeName]); } return partialSelection; @@ -159,9 +154,7 @@ class _ElementSelector { _addRefs(builder, classMap[className], node); } if (classPartialMap.containsKey(className)) { - if (partialSelection == null) { - partialSelection = new List<_ElementSelector>(); - } + if (partialSelection == null) partialSelection = <_ElementSelector>[]; partialSelection.add(classPartialMap[className]); } return partialSelection; @@ -187,15 +180,11 @@ class _ElementSelector { Map valuesPartialMap = attrValuePartialMap[attrName]; if (valuesPartialMap.containsKey('')) { - if (partialSelection == null) { - partialSelection = new List<_ElementSelector>(); - } + if (partialSelection == null) partialSelection = <_ElementSelector>[]; partialSelection.add(valuesPartialMap['']); } if (attrValue != '' && valuesPartialMap.containsKey(attrValue)) { - if (partialSelection == null) { - partialSelection = new List<_ElementSelector>(); - } + if (partialSelection == null) partialSelection = <_ElementSelector>[]; partialSelection.add(valuesPartialMap[attrValue]); } } @@ -209,10 +198,10 @@ class _ElementSelector { String _matchingKey(Iterable keys, String attrName) => keys.firstWhere((key) => _matchingKeyCache.putIfAbsent(key, - () => new RegExp('^${key.replaceAll('*', r'[\w\-]+')}\$')) - .hasMatch(attrName), orElse: () => null); + () => new RegExp('^${key.replaceAll('*', r'[\w\-]+')}\$')) + .hasMatch(attrName), orElse: () => null); - toString() => 'ElementSelector($name)'; + String toString() => 'ElementSelector($name)'; } List<_SelectorPart> _splitCss(String selector, Type type) { @@ -291,14 +280,14 @@ class DirectiveSelector { // Select node partialSelection = elementSelector.selectNode(builder, - partialSelection, element, nodeName); + partialSelection, element, nodeName); // Select .name if ((element.classes) != null) { for (var name in element.classes) { classes[name] = true; partialSelection = elementSelector.selectClass(builder, - partialSelection, element, name); + partialSelection, element, name); } } @@ -307,9 +296,7 @@ class DirectiveSelector { if (attrName.startsWith("on-")) { builder.onEvents[attrName] = value; - } - - if (attrName.startsWith("bind-")) { + } else if (attrName.startsWith("bind-")) { builder.bindAttrs[attrName] = value; } @@ -337,11 +324,11 @@ class DirectiveSelector { elementSelectors.forEach((_ElementSelector elementSelector) { classes.forEach((className, _) { partialSelection = elementSelector.selectClass(builder, - partialSelection, node, className); + partialSelection, node, className); }); attrs.forEach((attrName, value) { partialSelection = elementSelector.selectAttr(builder, - partialSelection, node, attrName, value); + partialSelection, node, attrName, value); }); }); } @@ -357,27 +344,24 @@ class DirectiveSelector { if (selectorRegExp.regexp.hasMatch(value)) { _directives[selectorRegExp.annotation].forEach((type) { builder.addDirective(new DirectiveRef(node, type, - selectorRegExp.annotation, value)); + selectorRegExp.annotation, value)); }); } } return builder.binder; } - ElementBinder matchComment(dom.Node node) { - return _binderFactory.builder().binder; - } + ElementBinder matchComment(dom.Node node) =>_binderFactory.builder().binder; } /** * Factory for creating a [DirectiveSelector]. */ @NgInjectableService() class DirectiveSelectorFactory { - ElementBinderFactory _binderFactory; + final ElementBinderFactory _binderFactory; DirectiveSelectorFactory(this._binderFactory); - DirectiveSelector selector(DirectiveMap directives) { - return new DirectiveSelector(directives, _binderFactory); - } + DirectiveSelector selector(DirectiveMap directives) => + new DirectiveSelector(directives, _binderFactory); } diff --git a/lib/core_dom/tagging_compiler.dart b/lib/core_dom/tagging_compiler.dart index d12a2d2e7..7d65bb9c0 100644 --- a/lib/core_dom/tagging_compiler.dart +++ b/lib/core_dom/tagging_compiler.dart @@ -19,8 +19,8 @@ class TaggingCompiler implements Compiler { List elementBinders) { var node = domCursor.current; - if (node.nodeType == 1) { - // If nodetype is a element, call selector matchElement. + if (node.nodeType == dom.Node.ELEMENT_NODE) { + // If the node is an element, call selector matchElement. // If text, call selector.matchText ElementBinder elementBinder = useExistingElementBinder == null ? @@ -39,13 +39,13 @@ class TaggingCompiler implements Compiler { return null; } - _compileNode(NodeCursor domCursor, - ElementBinder elementBinder, - DirectiveMap directives, - List elementBinders, - int parentElementBinderOffset, - bool isTopLevel, - TaggedElementBinder directParentElementBinder) { + void _compileNode(NodeCursor domCursor, + ElementBinder elementBinder, + DirectiveMap directives, + List elementBinders, + int parentElementBinderOffset, + bool isTopLevel, + TaggedElementBinder directParentElementBinder) { var node = domCursor.current; if (node.nodeType == dom.Node.ELEMENT_NODE) { TaggedElementBinder taggedElementBinder; @@ -156,18 +156,20 @@ class TaggingCompiler implements Compiler { return viewFactory; } - _isDummyBinder(TaggedElementBinder binder) => - binder.binder == null && binder.textBinders == null && !binder.isTopLevel; + bool _isDummyBinder(TaggedElementBinder binder) => + binder.binder == null && + binder.textBinders == null && + !binder.isTopLevel; - _removeUnusedBinders(List binders) { + List _removeUnusedBinders(List binders) { // In order to support text nodes with directiveless parents, we // add dummy ElementBinders to the list. After the entire template // has been compiled, we remove the dummies and update the offset indices - final output = []; - final List offsetMap = []; + final output = []; + final offsetMap = []; int outputIndex = 0; - for (var i = 0, ii = binders.length; i < ii; i++) { + for (var i = 0; i < binders.length; i++) { TaggedElementBinder binder = binders[i]; if (_isDummyBinder(binder)) { offsetMap.add(-2); diff --git a/lib/core_dom/tagging_view_factory.dart b/lib/core_dom/tagging_view_factory.dart index d6ebe1bbf..87bae610a 100644 --- a/lib/core_dom/tagging_view_factory.dart +++ b/lib/core_dom/tagging_view_factory.dart @@ -8,12 +8,10 @@ class TaggingViewFactory implements ViewFactory { TaggingViewFactory(this.templateNodes, this.elementBinders, this._perf); BoundViewFactory bind(Injector injector) => - new BoundViewFactory(this, injector); + new BoundViewFactory(this, injector); View call(Injector injector, [List nodes /* TODO: document fragment */]) { - if (nodes == null) { - nodes = cloneElements(templateNodes); - } + if (nodes == null) nodes = cloneElements(templateNodes); var timerId; try { assert((timerId = _perf.startTimer('ng.view')) != false); @@ -27,13 +25,17 @@ class TaggingViewFactory implements ViewFactory { _bindTagged(TaggedElementBinder tagged, rootInjector, elementBinders, View view, boundNode) { var binder = tagged.binder; - var parentInjector = tagged.parentBinderOffset == -1 ? rootInjector : elementBinders[tagged.parentBinderOffset].injector; + var parentInjector = tagged.parentBinderOffset == -1 ? + rootInjector: + elementBinders[tagged.parentBinderOffset].injector; assert(parentInjector != null); - tagged.injector = binder != null ? binder.bind(view, parentInjector, boundNode) : parentInjector; + tagged.injector = binder != null ? + binder.bind(view, parentInjector, boundNode): + parentInjector; if (tagged.textBinders != null) { - for (var k = 0, kk = tagged.textBinders.length; k < kk; k++) { + for (var k = 0; k < tagged.textBinders.length; k++) { TaggedTextBinder taggedText = tagged.textBinders[k]; taggedText.binder.bind(view, tagged.injector, boundNode.childNodes[taggedText.offsetIndex]); } @@ -44,7 +46,7 @@ class TaggingViewFactory implements ViewFactory { var directiveDefsByName = {}; var elementBinderIndex = 0; - for (int i = 0, ii = nodeList.length; i < ii; i++) { + for (int i = 0; i < nodeList.length; i++) { var node = nodeList[i]; // if node isn't attached to the DOM, create a parent for it. @@ -56,17 +58,18 @@ class TaggingViewFactory implements ViewFactory { parentNode.append(node); } - if (node.nodeType == 1) { + if (node.nodeType == dom.Node.ELEMENT_NODE) { var elts = node.querySelectorAll('.ng-binding'); // HACK: querySelectorAll doesn't return the node. var startIndex = node.classes.contains('ng-binding') ? -1 : 0; - for (int j = startIndex, jj = elts.length; j < jj; j++, elementBinderIndex++) { + for (int j = startIndex; j < elts.length; j++, elementBinderIndex++) { TaggedElementBinder tagged = elementBinders[elementBinderIndex]; var boundNode = j == -1 ? node : elts[j]; _bindTagged(tagged, rootInjector, elementBinders, view, boundNode); } - } else if (node.nodeType == 3 || node.nodeType == 8) { + } else if (node.nodeType == dom.Node.COMMENT_NODE || + node.nodeType == dom.Node.TEXT_NODE) { TaggedElementBinder tagged = elementBinders[elementBinderIndex]; assert(tagged.binder != null || tagged.isTopLevel); if (tagged.binder != null) { @@ -77,10 +80,8 @@ class TaggingViewFactory implements ViewFactory { throw "nodeType sadness ${node.nodeType}}"; } - if (fakeParent) { - // extract the node from the parentNode. - nodeList[i] = parentNode.nodes[0]; - } + // extract the node from the parentNode. + if (fakeParent) nodeList[i] = parentNode.nodes[0]; } return view; } diff --git a/lib/core_dom/view.dart b/lib/core_dom/view.dart index 72bde2651..983af5f38 100644 --- a/lib/core_dom/view.dart +++ b/lib/core_dom/view.dart @@ -38,7 +38,7 @@ class ViewPort { _viewsInsertAfter(view, insertAfter); _animate.insert(view.nodes, placeholder.parentNode, - insertBefore: previousNode.nextNode); + insertBefore: previousNode.nextNode); } void remove(View view) { @@ -61,7 +61,5 @@ class ViewPort { } dom.Node _lastNode(View insertAfter) => - insertAfter == null - ? placeholder - : insertAfter.nodes.last; + insertAfter == null ? placeholder : insertAfter.nodes.last; } diff --git a/lib/core_dom/view_factory.dart b/lib/core_dom/view_factory.dart index 603e444cf..4c4655202 100644 --- a/lib/core_dom/view_factory.dart +++ b/lib/core_dom/view_factory.dart @@ -25,91 +25,6 @@ abstract class ViewFactory implements Function { View call(Injector injector, [List elements]); } -/** - * [WalkingViewFactory] is used to create new [View]s. WalkingViewFactory is - * created by the [Compiler] as a result of compiling a template. - */ -class WalkingViewFactory implements ViewFactory { - final List elementBinders; - final List templateElements; - final Profiler _perf; - final Expando _expando; - - WalkingViewFactory(this.templateElements, this.elementBinders, this._perf, - this._expando) { - assert(elementBinders.every((ElementBinderTreeRef eb) => - eb is ElementBinderTreeRef)); - } - - BoundViewFactory bind(Injector injector) => - new BoundViewFactory(this, injector); - - View call(Injector injector, [List nodes]) { - if (nodes == null) nodes = cloneElements(templateElements); - var timerId; - try { - assert((timerId = _perf.startTimer('ng.view')) != false); - var view = new View(nodes, injector.get(EventHandler)); - _link(view, nodes, elementBinders, injector); - return view; - } finally { - assert(_perf.stopTimer(timerId) != false); - } - } - - View _link(View view, List nodeList, List elementBinders, - Injector parentInjector) { - - var preRenderedIndexOffset = 0; - var directiveDefsByName = {}; - - for (int i = 0; i < elementBinders.length; i++) { - // querySelectorAll('.ng-binding') should return a list of nodes in the - // same order as the elementBinders list. - - // keep a injector array -- - - var eb = elementBinders[i]; - int index = eb.offsetIndex; - - ElementBinderTree tree = eb.subtree; - - //List childElementBinders = eb.childElementBinders; - int nodeListIndex = index + preRenderedIndexOffset; - dom.Node node = nodeList[nodeListIndex]; - var binder = tree.binder; - - var timerId; - try { - assert((timerId = _perf.startTimer('ng.view.link', _html(node))) != false); - // if node isn't attached to the DOM, create a parent for it. - var parentNode = node.parentNode; - var fakeParent = false; - if (parentNode == null) { - fakeParent = true; - parentNode = new dom.DivElement()..append(node); - } - - var childInjector = binder != null ? - binder.bind(view, parentInjector, node) : - parentInjector; - - if (fakeParent) { - // extract the node from the parentNode. - nodeList[nodeListIndex] = parentNode.nodes[0]; - } - - if (tree.subtrees != null) { - _link(view, node.nodes, tree.subtrees, childInjector); - } - } finally { - assert(_perf.stopTimer(timerId) != false); - } - } - return view; - } -} - /** * ViewCache is used to cache the compilation of templates into [View]s. * It can be used synchronously if HTML is known or asynchronously if the @@ -250,7 +165,7 @@ class _ComponentFactory implements Function { class _AnchorAttrs extends NodeAttrs { DirectiveRef _directiveRef; - _AnchorAttrs(DirectiveRef this._directiveRef): super(null); + _AnchorAttrs(this._directiveRef): super(null); String operator [](name) => name == '.' ? _directiveRef.value : null; @@ -260,12 +175,8 @@ class _AnchorAttrs extends NodeAttrs { } String _html(obj) { - if (obj is String) { - return obj; - } - if (obj is List) { - return (obj as List).map((e) => _html(e)).join(); - } + if (obj is String) return obj; + if (obj is List) return (obj as List).map((e) => _html(e)).join(); if (obj is dom.Element) { var text = (obj as dom.Element).outerHtml; return text.substring(0, text.indexOf('>') + 1); diff --git a/lib/core_dom/walking_view_factory.dart b/lib/core_dom/walking_view_factory.dart new file mode 100644 index 000000000..cc56f1f03 --- /dev/null +++ b/lib/core_dom/walking_view_factory.dart @@ -0,0 +1,83 @@ +part of angular.core.dom_internal; + +/** + - * [WalkingViewFactory] is used to create new [View]s. WalkingViewFactory is + - * created by the [Compiler] as a result of compiling a template. + - */ +class WalkingViewFactory implements ViewFactory { + final List elementBinders; + final List templateElements; + final Profiler _perf; + final Expando _expando; + + WalkingViewFactory(this.templateElements, this.elementBinders, this._perf, + this._expando) { + assert(elementBinders.every((eb) => eb is ElementBinderTreeRef)); + } + + BoundViewFactory bind(Injector injector) => + new BoundViewFactory(this, injector); + + View call(Injector injector, [List nodes]) { + if (nodes == null) nodes = cloneElements(templateElements); + var timerId; + try { + assert((timerId = _perf.startTimer('ng.view')) != false); + var view = new View(nodes, injector.get(EventHandler)); + _link(view, nodes, elementBinders, injector); + return view; + } finally { + assert(_perf.stopTimer(timerId) != false); + } + } + + View _link(View view, List nodeList, List elementBinders, + Injector parentInjector) { + + var preRenderedIndexOffset = 0; + var directiveDefsByName = {}; + + for (int i = 0; i < elementBinders.length; i++) { + // querySelectorAll('.ng-binding') should return a list of nodes in the + // same order as the elementBinders list. + + // keep a injector array -- + + var eb = elementBinders[i]; + int index = eb.offsetIndex; + + ElementBinderTree tree = eb.subtree; + + //List childElementBinders = eb.childElementBinders; + int nodeListIndex = index + preRenderedIndexOffset; + dom.Node node = nodeList[nodeListIndex]; + var binder = tree.binder; + + var timerId; + try { + assert((timerId = _perf.startTimer('ng.view.link', _html(node))) != false); + // if node isn't attached to the DOM, create a parent for it. + var parentNode = node.parentNode; + var fakeParent = false; + if (parentNode == null) { + fakeParent = true; + parentNode = new dom.DivElement()..append(node); + } + + var childInjector = binder != null ? + binder.bind(view, parentInjector, node) : + parentInjector; + + // extract the node from the parentNode. + if (fakeParent) nodeList[nodeListIndex] = parentNode.nodes[0]; + + if (tree.subtrees != null) { + _link(view, node.nodes, tree.subtrees, childInjector); + } + } finally { + assert(_perf.stopTimer(timerId) != false); + } + } + return view; + } +} \ No newline at end of file diff --git a/lib/directive/module.dart b/lib/directive/module.dart index 8c5385419..22631e0af 100644 --- a/lib/directive/module.dart +++ b/lib/directive/module.dart @@ -50,8 +50,7 @@ class NgDirectiveModule extends Module { value(NgBind, null); value(NgBindTemplate, null); value(NgBindHtml, null); - factory(dom.NodeValidator, (_) => - new dom.NodeValidatorBuilder.common()); + factory(dom.NodeValidator, (_) => new dom.NodeValidatorBuilder.common()); value(NgClass, null); value(NgClassOdd, null); value(NgClassEven, null); diff --git a/test/core_dom/selector_spec.dart b/test/core_dom/selector_spec.dart index 891cac7a2..0f6dd5672 100644 --- a/test/core_dom/selector_spec.dart +++ b/test/core_dom/selector_spec.dart @@ -74,103 +74,110 @@ main() { }); it('should match directive on class', () { - expect(selector(element = e('
')), - toEqualsDirectiveInfos([ - { "selector": '.b', "value": null, "element": element} - ])); + expect( + selector(element = e('
')), + toEqualsDirectiveInfos([ + { "selector": '.b', "value": null, "element": element} + ])); }); it('should match directive on [attribute]', () { - expect(selector(element = e('
')), - toEqualsDirectiveInfos([ - { "selector": '[directive]', "value": 'abc', "element": element, + expect( + selector(element = e('
')), + toEqualsDirectiveInfos([ + { "selector": '[directive]', "value": 'abc', "element": element, "name": 'directive' }])); - expect(selector(element = e('
')), - toEqualsDirectiveInfos([ - { "selector": '[directive]', "value": '', "element": element, + expect( + selector(element = e('
')), + toEqualsDirectiveInfos([ + { "selector": '[directive]', "value": '', "element": element, "name": 'directive' }])); }); it('should match directive on element[attribute]', () { - expect(selector(element = e('')), - toEqualsDirectiveInfos([ - { "selector": 'b', "value": null, "element": element}, - { "selector": '[directive]', "value": 'abc', "element": element}, - { "selector": 'b[directive]', "value": 'abc', "element": element} + expect( + selector(element = e('')), + toEqualsDirectiveInfos([ + { "selector": 'b', "value": null, "element": element}, + { "selector": '[directive]', "value": 'abc', "element": element}, + { "selector": 'b[directive]', "value": 'abc', "element": element} ])); }); it('should match directive on [attribute=value]', () { - expect(selector(element = e('
')), - toEqualsDirectiveInfos([ - { "selector": '[directive]', "value": 'value', "element": element}, - { "selector": '[directive=value]', "value": 'value', "element": element} - ])); + expect( + selector(element = e('
')), + toEqualsDirectiveInfos([ + { "selector": '[directive]', "value": 'value', "element": element}, + { "selector": '[directive=value]', "value": 'value', "element": element} + ])); }); it('should match directive on element[attribute=value]', () { - expect(selector(element = e('')), - toEqualsDirectiveInfos([ - { "selector": 'b', "value": null, "element": element, "name": null}, - { "selector": '[directive]', "value": 'value', "element": element}, - { "selector": '[directive=value]', "value": 'value', "element": element}, - { "selector": 'b[directive]', "value": 'value', "element": element}, - { "selector": 'b[directive=value]', "value": 'value', "element": element} - ])); + expect(selector( + element = e('')), + toEqualsDirectiveInfos([ + { "selector": 'b', "value": null, "element": element, "name": null}, + { "selector": '[directive]', "value": 'value', "element": element}, + { "selector": '[directive=value]', "value": 'value', "element": element}, + { "selector": 'b[directive]', "value": 'value', "element": element}, + { "selector": 'b[directive=value]', "value": 'value', "element": element} + ])); }); it('should match attributes', () { - expect(selector(element = e('
')), - toEqualsDirectiveInfos([ - { "selector": '[*=/xyz/]', "value": 'attr=before-xyz-after', - "element": element, "name": 'attr'} - ])); + expect( + selector(element = e('
')), + toEqualsDirectiveInfos([ + { "selector": '[*=/xyz/]', "value": 'attr=before-xyz-after', + "element": element, "name": 'attr'} + ])); }); it('should match attribute names', () { - expect(selector(element = e('
')), - toEqualsDirectiveInfos([ - { "selector": '[wildcard-*]', "value": 'ignored', - "element": element, "name": 'wildcard-match'} - ])); + expect( + selector(element = e('
')), + toEqualsDirectiveInfos([ + { "selector": '[wildcard-*]', "value": 'ignored', + "element": element, "name": 'wildcard-match'} + ])); }); it('should sort by priority', () { - TemplateElementBinder eb = selector(element = e( - '')); + TemplateElementBinder eb = selector(element = + e('')); expect(eb, toEqualsDirectiveInfos( null, template: {"selector": "[structural]", "value": "", "element": element})); expect(eb.templateBinder, - toEqualsDirectiveInfos( - [ - { "selector": "[attribute]", "value": "", "element": element }, - { "selector": "[ignore-children]", "value": "", "element": element } - - ], + toEqualsDirectiveInfos([ + { "selector": "[attribute]", "value": "", "element": element }, + { "selector": "[ignore-children]", "value": "", "element": element }], component: { "selector": "component", "value": null, "element": element })); }); it('should match on multiple directives', () { - expect(selector(element = e('
')), - toEqualsDirectiveInfos([ - { "selector": '[directive]', "value": 'd', "element": element}, - { "selector": '[directive=d][foo=f]', "value": 'f', "element": element} - ])); + expect(selector( + element = e('
')), + toEqualsDirectiveInfos([ + { "selector": '[directive]', "value": 'd', "element": element}, + { "selector": '[directive=d][foo=f]', "value": 'f', "element": element} + ])); }); it('should match ng-model + required on the same element', () { expect( - selector(element = e('')), - toEqualsDirectiveInfos([ - { "selector": '[ng-model]', "value": 'val', "element": element}, - { "selector": '[probe]', "value": 'i', "element": element}, - { "selector": '[ng-model][required]', "value": 'true', "element": element}, - { "selector": 'input[type=text][ng-model]', "value": 'val', "element": element} - ])); + selector( + element = e('')), + toEqualsDirectiveInfos([ + { "selector": '[ng-model]', "value": 'val', "element": element}, + { "selector": '[probe]', "value": 'i', "element": element}, + { "selector": '[ng-model][required]', "value": 'true', "element": element}, + { "selector": 'input[type=text][ng-model]', "value": 'val', "element": element} + ])); }); it('should match two directives', () { @@ -183,11 +190,12 @@ main() { }); it('should match an two directives with the same selector', () { - expect(selector(element = e('
')), - toEqualsDirectiveInfos([ - { "selector": '[two-directives]', "value": '', "element": element}, - { "selector": '[two-directives]', "value": '', "element": element} - ])); + expect( + selector(element = e('
')), + toEqualsDirectiveInfos([ + { "selector": '[two-directives]', "value": '', "element": element}, + { "selector": '[two-directives]', "value": '', "element": element} + ])); }); it('should collect on-* attributes', () { @@ -207,9 +215,10 @@ main() { }); it('should match text', () { - expect(selector(element = e('before-abc-after')), - toEqualsDirectiveInfos([ - { "selector": ':contains(/abc/)', "value": 'before-abc-after', + expect( + selector(element = e('before-abc-after')), + toEqualsDirectiveInfos([ + { "selector": ':contains(/abc/)', "value": 'before-abc-after', "element": element, "name": '#text'} ])); }); From c7dcb8e2aa6d55a43fa31487c2c6a2e392379fab Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 4 Apr 2014 17:19:59 +0200 Subject: [PATCH 2/5] feat(NgTemplate): @NgTemplate replace @NgDirective(NgAnnotation.TRANSCLUDE_CHILDREN) 1- Transclusion syntax has changed: Before: @NgDirective( children: NgAnnotation.TRANSCLUDE_CHILDREN, map: const {'.': 'expression'}, ) After: @NgTemplate( mapping: 'expression', ) 2- NgAnnotation.children has been removed in facor of NgAnnotation.compileChildren Before: children: NgAnnotation.COMPILE_CHILDREN children: NgAnnotation.IGNORE_CHILDREN children: NgAnnotation.TRANSCLUDE_CHILDREN After: compileChildren: true compileChildren: false Use @NgTemplate (see the previous section) --- lib/application_factory.dart | 1 + lib/core/annotation.dart | 2 + lib/core/annotation_src.dart | 169 ++++++++++-------- lib/core/registry_dynamic.dart | 2 +- lib/core_dom/common.dart | 3 +- lib/core_dom/element_binder.dart | 69 +++---- lib/core_dom/element_binder_builder.dart | 66 ++++--- lib/core_dom/selector.dart | 19 +- lib/core_dom/tagging_compiler.dart | 2 +- lib/core_dom/view_factory.dart | 12 -- lib/core_dom/walking_compiler.dart | 2 +- lib/directive/module.dart | 4 +- lib/directive/ng_if.dart | 10 +- lib/directive/ng_non_bindable.dart | 2 +- ...g_template.dart => ng_plain_template.dart} | 8 +- lib/directive/ng_repeat.dart | 7 +- lib/directive/ng_switch.dart | 14 +- test/angular_spec.dart | 2 +- test/core/core_directive_spec.dart | 8 +- test/core_dom/compiler_spec.dart | 7 +- .../core_dom/element_binder_builder_spec.dart | 21 +-- test/core_dom/selector_spec.dart | 11 +- test/core_dom/view_spec.dart | 2 +- test/directive/ng_if_spec.dart | 4 +- ..._spec.dart => ng_plain_template_spec.dart} | 18 +- 25 files changed, 239 insertions(+), 226 deletions(-) rename lib/directive/{ng_template.dart => ng_plain_template.dart} (85%) rename test/directive/{ng_template_spec.dart => ng_plain_template_spec.dart} (80%) diff --git a/lib/application_factory.dart b/lib/application_factory.dart index 13768b6cb..8ff1bc881 100644 --- a/lib/application_factory.dart +++ b/lib/application_factory.dart @@ -48,6 +48,7 @@ import 'dart:html'; ], metaTargets: const [ NgInjectableService, + NgTemplate, NgDirective, NgController, NgComponent, diff --git a/lib/core/annotation.dart b/lib/core/annotation.dart index 8f07daaf0..6e7a7d8a2 100644 --- a/lib/core/annotation.dart +++ b/lib/core/annotation.dart @@ -14,6 +14,8 @@ export "package:angular/core/annotation_src.dart" show NgInjectableService, AbstractNgAnnotation, + AbstractNgAttrAnnotation, + NgTemplate, NgComponent, NgController, NgDirective, diff --git a/lib/core/annotation_src.dart b/lib/core/annotation_src.dart index 42c8387c1..d31c7df3a 100644 --- a/lib/core/annotation_src.dart +++ b/lib/core/annotation_src.dart @@ -35,7 +35,7 @@ class NgInjectableService { } /** - * Abstract supper class of [NgController], [NgComponent], and [NgDirective]. + * Abstract supper class of [NgTemplate] & [AbstractNgAttrAnnotation] */ abstract class AbstractNgAnnotation { /** @@ -57,30 +57,11 @@ abstract class AbstractNgAnnotation { * Specifies the compiler action to be taken on the child nodes of the * element which this currently being compiled. The values are: * - * * [COMPILE_CHILDREN] (*default*) - * * [TRANSCLUDE_CHILDREN] - * * [IGNORE_CHILDREN] - */ - @deprecated - final String children; - - /** - * Compile the child nodes of the element. This is the default. - */ - @deprecated - static const String COMPILE_CHILDREN = 'compile'; - /** - * Compile the child nodes for transclusion and makes available - * [BoundViewFactory], [ViewFactory] and [ViewPort] for injection. - */ - @deprecated - static const String TRANSCLUDE_CHILDREN = 'transclude'; - /** - * Do not compile/visit the child nodes. Angular markup on descendant nodes - * will not be processed. + * * [:true:] Compile the child nodes of the element. (default) + * * [:false:] Do not compile/visit the child nodes. Angular markup on + * descendant nodes will not be processed. */ - @deprecated - static const String IGNORE_CHILDREN = 'ignore'; + final bool compileChildren; /** * A directive/component controller class can be injected into other @@ -123,15 +104,78 @@ abstract class AbstractNgAnnotation { final Function module; /** - * Use map to define the mapping of DOM attributes to fields. + * Use the list to specify expressions containing attributes which are not + * included under [map] with '=' or '@' specification. This is used by + * angular transformer during deployment. + */ + final List exportExpressionAttrs; + + /** + * Use the list to specify expressions which are evaluated dynamically + * (ex. via [Scope.eval]) and are otherwise not statically discoverable. + * This is used by angular transformer during deployment. + */ + final List exportExpressions; + + const AbstractNgAnnotation({ + this.selector, + this.compileChildren: true, + this.visibility: NgDirective.LOCAL_VISIBILITY, + this.module, + this.exportExpressions: const [], + this.exportExpressionAttrs: const [] + }); + + toString() => selector; + get hashCode => selector.hashCode; + operator==(AbstractNgAnnotation other) => + other is AbstractNgAnnotation && selector == other.selector; +} + +/** + * todo(vicb): doc + */ +class NgTemplate extends AbstractNgAnnotation { + /** + * todo (vicb): doc + */ + final String mapping; + + const NgTemplate({this.mapping, + String selector, + Function module, + Visibility visibility, + List exportExpressions, + List exportExpressionAttrs}) + : super(selector: selector, + visibility: visibility, + module: module, + exportExpressions: exportExpressions, + exportExpressionAttrs: exportExpressionAttrs); + + NgTemplate cloneWithNewMapping(String newMapping) => + new NgTemplate(mapping: newMapping, + module: module, + selector: selector, + visibility: visibility, + exportExpressions: exportExpressions, + exportExpressionAttrs: exportExpressionAttrs); +} + +/** + * Abstract supper class of [NgController], [NgComponent], and [NgDirective]. + */ +abstract class AbstractNgAttrAnnotation extends AbstractNgAnnotation { + /** + * Use the [map] to define the mapping of DOM attributes to fields. * The map's key is the DOM attribute name (DOM attribute is in dash-case). * The Map's value consists of a mode prefix followed by an expression. * The destination expression will be evaluated against the instance of the * directive / component class. * * * `@` - Map the DOM attribute string. The attribute string will be taken - * literally or interpolated if it contains binding {{}} systax and assigned - * to the expression. (cost: 0 watches) + * literally or interpolated if it contains binding `{{}}` syntax and + * assigned to the expression. (cost: 0 watches) * * * `=>` - Treat the DOM attribute value as an expression. Set up a watch, * which will read the expression in the attribute and assign the value @@ -187,39 +231,24 @@ abstract class AbstractNgAnnotation { */ final Map map; - /** - * Use the list to specify expressions containing attributes which are not - * included under [map] with '=' or '@' specification. This is used by - * angular transformer during deployment. - */ - final List exportExpressionAttrs; - - /** - * Use the list to specify expressions which are evaluated dynamically - * (ex. via [Scope.eval]) and are otherwise not statically discoverable. - * This is used by angular transformer during deployment. - */ - final List exportExpressions; - - const AbstractNgAnnotation({ - this.selector, - this.children: AbstractNgAnnotation.COMPILE_CHILDREN, - this.visibility: NgDirective.LOCAL_VISIBILITY, - this.module, - this.map: const {}, - this.exportExpressions: const [], - this.exportExpressionAttrs: const [] - }); - - toString() => selector; - get hashCode => selector.hashCode; - operator==(other) => - other is AbstractNgAnnotation && selector == other.selector; + const AbstractNgAttrAnnotation({ + String selector, + bool compileChildren: true, + Visibility visibility: NgDirective.LOCAL_VISIBILITY, + Function module, + this.map: const {}, + List exportExpressions: const [], + List exportExpressionAttrs: const []}) + : super(selector: selector, + compileChildren: compileChildren, + visibility: visibility, + module: module, + exportExpressions: exportExpressions, + exportExpressionAttrs: exportExpressionAttrs); - AbstractNgAnnotation _cloneWithNewMap(newMap); + AbstractNgAnnotation _cloneWithNewMap(Map newMap); } - bool _applyAuthorStylesDeprecationWarningPrinted = false; bool _resetStyleInheritanceDeprecationWarningPrinted = false; @@ -239,7 +268,7 @@ bool _resetStyleInheritanceDeprecationWarningPrinted = false; * * `detach()` - Called on when owning scope is destroyed. * * `onShadowRoot(ShadowRoot shadowRoot)` - Called when [ShadowRoot] is loaded. */ -class NgComponent extends AbstractNgAnnotation { +class NgComponent extends AbstractNgAttrAnnotation { /** * Inlined HTML template for the component. */ @@ -312,7 +341,7 @@ class NgComponent extends AbstractNgAnnotation { _applyAuthorStyles = applyAuthorStyles, _resetStyleInheritance = resetStyleInheritance, super(selector: selector, - children: AbstractNgAnnotation.COMPILE_CHILDREN, + compileChildren: true, visibility: visibility, map: map, module: module, @@ -323,7 +352,7 @@ class NgComponent extends AbstractNgAnnotation { const [] : _cssUrls is List ? _cssUrls : [_cssUrls]; - AbstractNgAnnotation _cloneWithNewMap(newMap) => + NgComponent _cloneWithNewMap(Map newMap) => new NgComponent(template: template, templateUrl: templateUrl, cssUrl: cssUrls, @@ -351,7 +380,7 @@ class NgComponent extends AbstractNgAnnotation { * * `attach()` - Called on first [Scope.apply()]. * * `detach()` - Called on when owning scope is destroyed. */ -class NgDirective extends AbstractNgAnnotation { +class NgDirective extends AbstractNgAttrAnnotation { /// The directive can only be injected to other directives on the same element. static const Visibility LOCAL_VISIBILITY = localVisibility; @@ -365,7 +394,7 @@ class NgDirective extends AbstractNgAnnotation { */ static const Visibility DIRECT_CHILDREN_VISIBILITY = directChildrenVisibility; - const NgDirective({children: AbstractNgAnnotation.COMPILE_CHILDREN, + const NgDirective({compileChildren: true, map, selector, module, @@ -373,15 +402,15 @@ class NgDirective extends AbstractNgAnnotation { exportExpressions, exportExpressionAttrs}) : super(selector: selector, - children: children, + compileChildren: compileChildren, visibility: visibility, map: map, module: module, exportExpressions: exportExpressions, exportExpressionAttrs: exportExpressionAttrs); - AbstractNgAnnotation _cloneWithNewMap(newMap) => - new NgDirective(children: children, + NgDirective _cloneWithNewMap(newMap) => + new NgDirective(compileChildren: compileChildren, map: newMap, module: module, selector: selector, @@ -407,7 +436,7 @@ class NgDirective extends AbstractNgAnnotation { * * `attach()` - Called on first [Scope.apply()]. * * `detach()` - Called on when owning scope is destroyed. */ -class NgController extends NgDirective { +class NgController extends AbstractNgAttrAnnotation { /** * An expression under which the controller instance will be published into. * This allows the expressions in the template to be referring to controller @@ -415,7 +444,7 @@ class NgController extends NgDirective { */ final String publishAs; - const NgController({children: AbstractNgAnnotation.COMPILE_CHILDREN, + const NgController({compileChildren: true, this.publishAs, map, module, @@ -424,15 +453,15 @@ class NgController extends NgDirective { exportExpressions, exportExpressionAttrs}) : super(selector: selector, - children: children, + compileChildren: compileChildren, visibility: visibility, map: map, module: module, exportExpressions: exportExpressions, exportExpressionAttrs: exportExpressionAttrs); - AbstractNgAnnotation _cloneWithNewMap(newMap) => - new NgController(children: children, + NgController _cloneWithNewMap(newMap) => + new NgController(compileChildren: compileChildren, publishAs: publishAs, module: module, map: newMap, diff --git a/lib/core/registry_dynamic.dart b/lib/core/registry_dynamic.dart index 4af594079..53d5768b3 100644 --- a/lib/core/registry_dynamic.dart +++ b/lib/core/registry_dynamic.dart @@ -46,7 +46,7 @@ class DynamicMetadataExtractor implements MetadataExtractor { var attrName = ann.attrName; if (newMap.containsKey(attrName)) { throw 'Mapping for attribute $attrName is already defined (while ' - 'processing annottation for field $fieldName of $type)'; + 'processing annotation for field $fieldName of $type)'; } newMap[attrName] = '${mappingSpec(ann)}$fieldName'; }); diff --git a/lib/core_dom/common.dart b/lib/core_dom/common.dart index 24874c5c2..d85a2c670 100644 --- a/lib/core_dom/common.dart +++ b/lib/core_dom/common.dart @@ -8,7 +8,8 @@ class MappingParts { final String dstExpression; final String originalValue; - const MappingParts(this.attrName, this.mode, this.dstExpression, this.originalValue); + const MappingParts(this.attrName, this.mode, this.dstExpression, + this.originalValue); } class DirectiveRef { diff --git a/lib/core_dom/element_binder.dart b/lib/core_dom/element_binder.dart index fda4b5724..5ab6e7e12 100644 --- a/lib/core_dom/element_binder.dart +++ b/lib/core_dom/element_binder.dart @@ -48,17 +48,14 @@ class ElementBinder { final DirectiveRef component; - // Can be either COMPILE_CHILDREN or IGNORE_CHILDREN - final String childMode; + final bool compileChildren; - ElementBinder(this._perf, this._expando, this._parser, this.component, this.decorators, - this.onEvents, this.bindAttrs, this.childMode); + ElementBinder(this._perf, this._expando, this._parser, this.component, + this.decorators, this.onEvents, this.bindAttrs, + this.compileChildren); final bool hasTemplate = false; - bool get shouldCompileChildren => - childMode == AbstractNgAnnotation.COMPILE_CHILDREN; - var _directiveCache; List get _usableDirectiveRefs { @@ -73,25 +70,33 @@ class ElementBinder { bool get hasDirectivesOrEvents => _usableDirectiveRefs.isNotEmpty || onEvents.isNotEmpty; - _createAttrMappings(controller, scope, DirectiveRef ref, nodeAttrs, filters, tasks) { + void _createAttrMappings(controller, scope, DirectiveRef ref, nodeAttrs, filters, tasks) { + var expression = ref.value; + ref.mappings.forEach((MappingParts p) { var attrName = p.attrName; + var isTemplate = nodeAttrs == null; var dstExpression = p.dstExpression; - if (nodeAttrs == null) nodeAttrs = new _AnchorAttrs(ref); Expression dstPathFn = _parser(dstExpression); if (!dstPathFn.isAssignable) { - throw "Expression '$dstExpression' is not assignable in mapping '${p.originalValue}' " - "for attribute '$attrName'."; + var msg = "Expression '$dstExpression' is not assignable in mapping " + "'${p.originalValue}'"; + throw attrName == null ? msg : msg + " for attribute '$attrName'."; } switch (p.mode) { case '@': // string var taskId = tasks.registerTask(); - nodeAttrs.observe(attrName, (value) { - dstPathFn.assign(controller, value); + if (isTemplate) { + dstPathFn.assign(controller, expression); tasks.completeTask(taskId); - }); + } else { + nodeAttrs.observe(attrName, (value) { + dstPathFn.assign(controller, value); + tasks.completeTask(taskId); + }); + } break; case '<=>': // two-way @@ -124,32 +129,36 @@ class ElementBinder { break; case '=>': // one-way - if (nodeAttrs[attrName] == null) return; + if (!isTemplate) expression = nodeAttrs[attrName]; + if (expression == null) return; var taskId = tasks.registerTask(); - Expression attrExprFn = _parser(nodeAttrs[attrName]); - scope.watch(nodeAttrs[attrName], (v, _) { + scope.watch(expression, (v, _) { dstPathFn.assign(controller, v); tasks.completeTask(taskId); }, filters: filters); break; case '=>!': // one-way, one-time - if (nodeAttrs[attrName] == null) return; + if (!isTemplate) expression = nodeAttrs[attrName]; + if (expression == null) return; - Expression attrExprFn = _parser(nodeAttrs[attrName]); var watch; - watch = scope.watch(nodeAttrs[attrName], (value, _) { - if (dstPathFn.assign(controller, value) != null) { + watch = scope.watch(expression, (v, _) { + if (dstPathFn.assign(controller, v) != null) { watch.remove(); } }, filters: filters); break; case '&': // callback + if (!isTemplate) expression = nodeAttrs[attrName]; dstPathFn.assign(controller, - _parser(nodeAttrs[attrName]).bind(scope.context, ScopeLocals.wrapper)); + _parser(expression).bind(scope.context, ScopeLocals.wrapper)); break; + + default: + throw "Unsupported mode '${p.mode}'"; } }); } @@ -168,9 +177,9 @@ class ElementBinder { scope.context[(ref.annotation as NgController).publishAs] = controller; } - var tasks = new _TaskList(controller is NgAttachAware ? () { - if (scope.isAttached) controller.attach(); - } : null); + var tasks = new _TaskList(controller is NgAttachAware ? + () { if (scope.isAttached) controller.attach(); } : + null); _createAttrMappings(controller, scope, ref, nodeAttrs, filters, tasks); @@ -310,12 +319,12 @@ class ElementBinder { * Private class used for managing controller.attach() calls */ class _TaskList { - var onDone; + final Function _onDone; final List _tasks = []; bool isDone = false; - _TaskList(this.onDone) { - if (onDone == null) isDone = true; + _TaskList(this._onDone) { + if (_onDone == null) isDone = true; } int registerTask() { @@ -328,12 +337,12 @@ class _TaskList { if (isDone) return; _tasks[id] = true; if (_tasks.every((a) => a)) { - onDone(); + _onDone(); isDone = true; } } - doneRegistering() { + void doneRegistering() { completeTask(registerTask()); } } diff --git a/lib/core_dom/element_binder_builder.dart b/lib/core_dom/element_binder_builder.dart index d81ad7005..bf88ff522 100644 --- a/lib/core_dom/element_binder_builder.dart +++ b/lib/core_dom/element_binder_builder.dart @@ -13,10 +13,10 @@ class ElementBinderFactory { ElementBinder binder(ElementBinderBuilder b) => new ElementBinder(_perf, _expando, _parser, - b.component, b.decorators, b.onEvents, b.bindAttrs, b.childMode); + b.component, b.decorators, b.onEvents, b.bindAttrs, b.compileChildren); TemplateElementBinder templateBinder(ElementBinderBuilder b, ElementBinder transclude) => new TemplateElementBinder(_perf, _expando, _parser, - b.template, transclude, b.onEvents, b.bindAttrs, b.childMode); + b.template, transclude, b.onEvents, b.bindAttrs, b.compileChildren); } /** @@ -37,39 +37,31 @@ class ElementBinderBuilder { DirectiveRef component; - // Can be either COMPILE_CHILDREN or IGNORE_CHILDREN - String childMode = AbstractNgAnnotation.COMPILE_CHILDREN; + bool compileChildren = true; ElementBinderBuilder(this._factory); - addDirective(DirectiveRef ref) { + void addDirective(DirectiveRef ref) { var annotation = ref.annotation; - var children = annotation.children; - if (annotation.children == AbstractNgAnnotation.TRANSCLUDE_CHILDREN) { + compileChildren = annotation.compileChildren; + + if (annotation is NgTemplate) { template = ref; - } else if (annotation is NgComponent) { - component = ref; + _addMapping(ref, (annotation as NgTemplate).mapping); } else { - decorators.add(ref); - } - - if (annotation.children == AbstractNgAnnotation.IGNORE_CHILDREN) { - childMode = annotation.children; - } - - if (annotation.map != null) annotation.map.forEach((attrName, mapping) { - Match match = _MAPPING.firstMatch(mapping); - if (match == null) { - throw "Unknown mapping '$mapping' for attribute '$attrName'."; + if (annotation is NgComponent) { + component = ref; + } else { + decorators.add(ref); } - var mode = match[1]; - var dstPath = match[2]; - - String dstExpression = dstPath.isEmpty ? attrName : dstPath; - - ref.mappings.add(new MappingParts(attrName, mode, dstExpression, mapping)); - }); + annotation = annotation as AbstractNgAttrAnnotation; + if (annotation.map != null) { + annotation.map.forEach((attrName, mapping) { + _addMapping(ref, mapping, attrName); + }); + } + } } ElementBinder get binder { @@ -79,7 +71,25 @@ class ElementBinderBuilder { } else { return _factory.binder(this); } + } + + void _addMapping(DirectiveRef ref, String mapping, [String attrName]) { + if (mapping == null) return; + Match match = _MAPPING.firstMatch(mapping); + if (match == null) { + throw "Unknown mapping '$mapping' for attribute '$attrName'."; + } + var mode = match[1]; + var dstPath = match[2]; - return mappingFn; + if (dstPath.isEmpty && attrName != null) dstPath = attrName; + + ref.mappings.add(new MappingParts(attrName, mode, dstPath, mapping)); + } + + void _addRefs(List<_Directive> directives, dom.Node node, [String attrValue]) { + directives.forEach((directive) { + addDirective(new DirectiveRef(node, directive.type, directive.annotation, attrValue)); + }); } } diff --git a/lib/core_dom/selector.dart b/lib/core_dom/selector.dart index 9b290ca0b..5ba5e77af 100644 --- a/lib/core_dom/selector.dart +++ b/lib/core_dom/selector.dart @@ -71,13 +71,6 @@ class _SelectorPart { : element; } -_addRefs(ElementBinderBuilder builder, List<_Directive> directives, dom.Node node, - [String attrValue]) { - directives.forEach((directive) { - builder.addDirective(new DirectiveRef(node, directive.type, directive.annotation, attrValue)); - }); -} - class _ElementSelector { final String name; @@ -138,7 +131,7 @@ class _ElementSelector { List<_ElementSelector> partialSelection, dom.Node node, String nodeName) { if (elementMap.containsKey(nodeName)) { - _addRefs(builder, elementMap[nodeName], node); + builder._addRefs(elementMap[nodeName], node); } if (elementPartialMap.containsKey(nodeName)) { if (partialSelection == null) partialSelection = <_ElementSelector>[]; @@ -151,7 +144,7 @@ class _ElementSelector { List<_ElementSelector> partialSelection, dom.Node node, String className) { if (classMap.containsKey(className)) { - _addRefs(builder, classMap[className], node); + builder._addRefs(classMap[className], node); } if (classPartialMap.containsKey(className)) { if (partialSelection == null) partialSelection = <_ElementSelector>[]; @@ -170,10 +163,10 @@ class _ElementSelector { if (matchingKey != null) { Map> valuesMap = attrValueMap[matchingKey]; if (valuesMap.containsKey('')) { - _addRefs(builder, valuesMap[''], node, attrValue); + builder._addRefs(valuesMap[''], node, attrValue); } if (attrValue != '' && valuesMap.containsKey(attrValue)) { - _addRefs(builder, valuesMap[attrValue], node, attrValue); + builder._addRefs(valuesMap[attrValue], node, attrValue); } } if (attrValuePartialMap.containsKey(attrName)) { @@ -279,8 +272,8 @@ class DirectiveSelector { } // Select node - partialSelection = elementSelector.selectNode(builder, - partialSelection, element, nodeName); + partialSelection = elementSelector.selectNode(builder, partialSelection, + element, nodeName); // Select .name if ((element.classes) != null) { diff --git a/lib/core_dom/tagging_compiler.dart b/lib/core_dom/tagging_compiler.dart index 7d65bb9c0..72e622a60 100644 --- a/lib/core_dom/tagging_compiler.dart +++ b/lib/core_dom/tagging_compiler.dart @@ -60,7 +60,7 @@ class TaggingCompiler implements Compiler { taggedElementBinderIndex = parentElementBinderOffset; } - if (elementBinder.shouldCompileChildren) { + if (elementBinder.compileChildren) { if (domCursor.descend()) { var addedDummy = false; if (taggedElementBinder == null) { diff --git a/lib/core_dom/view_factory.dart b/lib/core_dom/view_factory.dart index 4c4655202..bc0b6395f 100644 --- a/lib/core_dom/view_factory.dart +++ b/lib/core_dom/view_factory.dart @@ -162,18 +162,6 @@ class _ComponentFactory implements Function { } } -class _AnchorAttrs extends NodeAttrs { - DirectiveRef _directiveRef; - - _AnchorAttrs(this._directiveRef): super(null); - - String operator [](name) => name == '.' ? _directiveRef.value : null; - - void observe(String attributeName, _AttributeChanged notifyFn) { - notifyFn(attributeName == '.' ? _directiveRef.value : null); - } -} - String _html(obj) { if (obj is String) return obj; if (obj is List) return (obj as List).map((e) => _html(e)).join(); diff --git a/lib/core_dom/walking_compiler.dart b/lib/core_dom/walking_compiler.dart index 3a49fa190..9a86c8be8 100644 --- a/lib/core_dom/walking_compiler.dart +++ b/lib/core_dom/walking_compiler.dart @@ -45,7 +45,7 @@ class WalkingCompiler implements Compiler { templateBinder.template, templateBinder.templateBinder, directives); } - if (elementBinder.shouldCompileChildren) { + if (elementBinder.compileChildren) { if (domCursor.descend()) { templateCursor.descend(); diff --git a/lib/directive/module.dart b/lib/directive/module.dart index 22631e0af..76f38ee85 100644 --- a/lib/directive/module.dart +++ b/lib/directive/module.dart @@ -33,7 +33,7 @@ part 'ng_control.dart'; part 'ng_model.dart'; part 'ng_pluralize.dart'; part 'ng_repeat.dart'; -part 'ng_template.dart'; +part 'ng_plain_template.dart'; part 'ng_show_hide.dart'; part 'ng_src_boolean.dart'; part 'ng_style.dart'; @@ -86,7 +86,7 @@ class NgDirectiveModule extends Module { value(NgEvent, null); value(NgStyle, null); value(NgNonBindable, null); - value(NgTemplate, null); + value(NgPlainTemplate, null); value(NgControl, new NgNullControl()); value(NgForm, new NgNullForm()); diff --git a/lib/directive/ng_if.dart b/lib/directive/ng_if.dart index f0a0ce18e..07f7a5b4d 100644 --- a/lib/directive/ng_if.dart +++ b/lib/directive/ng_if.dart @@ -89,10 +89,9 @@ abstract class _NgUnlessIfAttrDirectiveBase { * * */ -@NgDirective( - children: AbstractNgAnnotation.TRANSCLUDE_CHILDREN, +@NgTemplate( selector:'[ng-if]', - map: const {'.': '=>condition'}) + mapping: '=>condition') class NgIf extends _NgUnlessIfAttrDirectiveBase { NgIf(BoundViewFactory boundViewFactory, ViewPort viewPort, @@ -150,10 +149,9 @@ class NgIf extends _NgUnlessIfAttrDirectiveBase { * * */ -@NgDirective( - children: AbstractNgAnnotation.TRANSCLUDE_CHILDREN, +@NgTemplate( selector:'[ng-unless]', - map: const {'.': '=>condition'}) + mapping: '=>condition') class NgUnless extends _NgUnlessIfAttrDirectiveBase { NgUnless(BoundViewFactory boundViewFactory, diff --git a/lib/directive/ng_non_bindable.dart b/lib/directive/ng_non_bindable.dart index 975e154d8..cf77be1ec 100644 --- a/lib/directive/ng_non_bindable.dart +++ b/lib/directive/ng_non_bindable.dart @@ -20,5 +20,5 @@ part of angular.directive; */ @NgDirective( selector: '[ng-non-bindable]', - children: AbstractNgAnnotation.IGNORE_CHILDREN) + compileChildren: false) class NgNonBindable {} diff --git a/lib/directive/ng_template.dart b/lib/directive/ng_plain_template.dart similarity index 85% rename from lib/directive/ng_template.dart rename to lib/directive/ng_plain_template.dart index 4ea85ac63..941d688bf 100644 --- a/lib/directive/ng_template.dart +++ b/lib/directive/ng_plain_template.dart @@ -1,7 +1,7 @@ part of angular.directive; /** - * The [NgTemplateElement] allows one to preload an Angular template + * The [NgPlainTemplate] allows one to preload an Angular template * into the [TemplateCache]. It works on `