diff --git a/lib/core_dom/compiler.dart b/lib/core_dom/compiler.dart index 8f9557121..4cc658968 100644 --- a/lib/core_dom/compiler.dart +++ b/lib/core_dom/compiler.dart @@ -3,6 +3,8 @@ part of angular.core.dom_internal; var _Compiler_call = traceCreateScope('Compiler#call()'); var _Compiler_subTemplate = traceCreateScope('Compiler#subTemplate()'); +const VIEW_PORT_TYPE = 'ng/viewport'; + @Injectable() class Compiler implements Function { final Profiler _perf; @@ -68,7 +70,7 @@ class Compiler implements Function { taggedElementBinder = _addBinder(elementBinders, new TaggedElementBinder(elementBinder, parentElementBinderOffset, isTopLevel)); taggedElementBinderIndex = elementBinders.length - 1; - node.classes.add('ng-binding'); + node.classes.add(NG_BINDING); } else { taggedElementBinder = null; taggedElementBinderIndex = parentElementBinderOffset; @@ -93,7 +95,7 @@ class Compiler implements Function { // // To avoid array chrun, we remove all dummy binders at the // end of the compilation process. - node.classes.add('ng-binding'); + node.classes.add(NG_BINDING); } domCursor.ascend(); } @@ -142,10 +144,7 @@ class Compiler implements Function { ElementBinder transcludedElementBinder, DirectiveMap directives) { var s = traceEnter(_Compiler_subTemplate); - var anchorName = directiveRef.annotation.selector + - (directiveRef.value != null ? '=' + directiveRef.value : ''); - - var transcludeCursor = templateCursor.replaceWithAnchor(anchorName); + var transcludeCursor = templateCursor.replaceWithAnchor(_anchorAttrs(directiveRef)); var elementBinders = []; _compileView(transcludeCursor, transcludedElementBinder, directives, -1, null, elementBinders, true); @@ -156,6 +155,14 @@ class Compiler implements Function { return viewFactory; } + Map _anchorAttrs(DirectiveRef directiveRef) { + return { + 'type': VIEW_PORT_TYPE, + 'directive': directiveRef.type.toString(), + 'value' : directiveRef.value + }; + } + List _removeUnusedBinders(List binders) { // In order to support text nodes with directiveless parents, we // add dummy ElementBinders to the list. After the entire template diff --git a/lib/core_dom/element_binder.dart b/lib/core_dom/element_binder.dart index 5c90afe77..411ce08f1 100644 --- a/lib/core_dom/element_binder.dart +++ b/lib/core_dom/element_binder.dart @@ -261,7 +261,7 @@ class ElementBinder { DirectiveInjector bind(View view, Scope scope, DirectiveInjector parentInjector, dom.Node node, EventHandler eventHandler, Animate animate) { - var nodeAttrs = node is dom.Element ? new NodeAttrs(node) : null; + var nodeAttrs = (node is dom.Element && !_isViewPort(node)) ? new NodeAttrs(node) : null; var directiveRefs = _usableDirectiveRefs; if (!hasDirectivesOrEvents) return parentInjector; @@ -329,6 +329,10 @@ class ElementBinder { return nodeInjector; } + bool _isViewPort(dom.Node node) => + node is dom.TemplateElement && + node.attributes["type"] == VIEW_PORT_TYPE; + String toString() => "[ElementBinder decorators:$decorators]"; } diff --git a/lib/core_dom/node_cursor.dart b/lib/core_dom/node_cursor.dart index ef7523097..3d469de88 100644 --- a/lib/core_dom/node_cursor.dart +++ b/lib/core_dom/node_cursor.dart @@ -29,10 +29,12 @@ class NodeCursor { index = stack.removeLast(); } - NodeCursor replaceWithAnchor(String name) { + NodeCursor replaceWithAnchor(Map attrs) { var element = current; var parent = element.parentNode; - var anchor = new dom.Comment('ANCHOR: $name'); + var anchor = new dom.TemplateElement() + ..classes.add(NG_BINDING) + ..attributes.addAll(attrs); if (parent != null) parent.insertBefore(anchor, element); element.remove(); elements[index] = anchor; diff --git a/lib/core_dom/view_factory.dart b/lib/core_dom/view_factory.dart index a3e2f07d5..95ac9f912 100644 --- a/lib/core_dom/view_factory.dart +++ b/lib/core_dom/view_factory.dart @@ -4,6 +4,10 @@ var _ViewFactory_call = traceCreateScope('ViewFactory#call(ascii html)'); var _ViewFactory_bind = traceCreateScope('ViewFactory#bind()'); var _ViewFactory_querySelectorAll = traceCreateScope('ViewFactory#querySelectorAll()'); +const NG_BINDING = 'ng-binding'; +const NG_BINDING_SELECTOR = '.$NG_BINDING'; + + /** * BoundViewFactory is a [ViewFactory] which does not need Injector because * it is pre-bound to an injector from the parent. This means that this @@ -125,7 +129,7 @@ class ViewFactory implements Function { if (linkingInfo.ngBindingChildren) { var s = traceEnter(_ViewFactory_querySelectorAll); - var elts = (node as dom.Element).querySelectorAll('.ng-binding'); + var elts = (node as dom.Element).querySelectorAll(NG_BINDING_SELECTOR); traceLeave(s); for (int j = 0; j < elts.length; j++, elementBinderIndex++) { TaggedElementBinder tagged = elementBinders[elementBinderIndex]; @@ -182,9 +186,9 @@ computeNodeLinkingInfos(List nodeList) { bool isElement = node.nodeType == dom.Node.ELEMENT_NODE; list[i] = new NodeLinkingInfo( - isElement && (node as dom.Element).classes.contains('ng-binding'), + isElement && (node as dom.Element).classes.contains(NG_BINDING), isElement, - isElement && (node as dom.Element).querySelectorAll('.ng-binding').length > 0); + isElement && (node as dom.Element).querySelector(NG_BINDING_SELECTOR) != null); } return list; } diff --git a/test/_specs.dart b/test/_specs.dart index f6c024973..226c76ecd 100644 --- a/test/_specs.dart +++ b/test/_specs.dart @@ -3,6 +3,7 @@ library ng_specs; import 'dart:html' hide Animation; import 'package:angular/angular.dart'; +import 'package:angular/core_dom/module_internal.dart'; import 'package:angular/mock/module.dart'; import 'package:guinness/guinness_html.dart' as gns; @@ -104,9 +105,9 @@ void iit(String name, Function fn) { _removeNgBinding(node) { if (node is Element) { var el = node.clone(true) as Element; - el.classes.remove('ng-binding'); - el.querySelectorAll(".ng-binding").forEach((Element e) { - e.classes.remove('ng-binding'); + el.classes.remove(NG_BINDING); + el.querySelectorAll(NG_BINDING_SELECTOR).forEach((Element e) { + e.classes.remove(NG_BINDING); }); return el; } diff --git a/test/core_dom/compiler_spec.dart b/test/core_dom/compiler_spec.dart index 916225cdc..fd9112152 100644 --- a/test/core_dom/compiler_spec.dart +++ b/test/core_dom/compiler_spec.dart @@ -35,6 +35,9 @@ forAllCompilersAndComponentFactories(fn) { } void main() { + String template(Type type, String value) => + ''; + withElementProbeConfig((compilerType) => describe('TranscludingComponentFactory', () { TestBed _; @@ -133,9 +136,12 @@ void main() { _.rootScope.context['items'] = []; _.rootScope.apply(); - expect(element).toHaveHtml(''); + + expect(element).toHaveHtml(template(NgRepeat, 'item in items')); }); + + it('should compile a text child of a basic repeater', () { var element = _.compile( '
' + @@ -174,7 +180,7 @@ void main() { _.rootScope.context['items'] = []; _.rootScope.apply(); - expect(element).toHaveHtml(''); + expect(element).toHaveHtml(template(NgRepeat, 'item in items')); }); it('should compile text', (Compiler compile) { diff --git a/test/core_dom/node_cursor_spec.dart b/test/core_dom/node_cursor_spec.dart index 731495e2b..94665a3aa 100644 --- a/test/core_dom/node_cursor_spec.dart +++ b/test/core_dom/node_cursor_spec.dart @@ -19,25 +19,25 @@ main() { it('should allow single level traversal', () { var cursor = new NodeCursor([a, b]); - expect(cursor.current, equals(a)); - expect(cursor.moveNext(), equals(true)); - expect(cursor.current, equals(b)); - expect(cursor.moveNext(), equals(false)); + expect(cursor.current).toEqual(a); + expect(cursor.moveNext()).toEqual(true); + expect(cursor.current).toEqual(b); + expect(cursor.moveNext()).toEqual(false); }); it('should descend and ascend', () { var cursor = new NodeCursor([d, c]); - expect(cursor.descend(), equals(true)); - expect(cursor.current, equals(a)); - expect(cursor.moveNext(), equals(true)); - expect(cursor.current, equals(b)); - expect(cursor.moveNext(), equals(false)); + expect(cursor.descend()).toEqual(true); + expect(cursor.current).toEqual(a); + expect(cursor.moveNext()).toEqual(true); + expect(cursor.current).toEqual(b); + expect(cursor.moveNext()).toEqual(false); cursor.ascend(); - expect(cursor.moveNext(), equals(true)); - expect(cursor.current, equals(c)); - expect(cursor.moveNext(), equals(false)); + expect(cursor.moveNext()).toEqual(true); + expect(cursor.current).toEqual(c); + expect(cursor.moveNext()).toEqual(false); }); it('should descend and ascend two levels', () { @@ -50,34 +50,36 @@ main() { l2.append(g); var cursor = new NodeCursor([l1, c]); - expect(cursor.descend(), equals(true)); - expect(cursor.current, equals(l2)); - expect(cursor.descend(), equals(true)); - expect(cursor.current, equals(g)); + expect(cursor.descend()).toEqual(true); + expect(cursor.current).toEqual(l2); + expect(cursor.descend()).toEqual(true); + expect(cursor.current).toEqual(g); cursor.ascend(); - expect(cursor.moveNext(), equals(true)); - expect(cursor.current, equals(f)); - expect(cursor.moveNext(), equals(false)); + expect(cursor.moveNext()).toEqual(true); + expect(cursor.current).toEqual(f); + expect(cursor.moveNext()).toEqual(false); cursor.ascend(); - expect(cursor.moveNext(), equals(true)); - expect(cursor.current, equals(c)); - expect(cursor.moveNext(), equals(false)); + expect(cursor.moveNext()).toEqual(true); + expect(cursor.current).toEqual(c); + expect(cursor.moveNext()).toEqual(false); }); it('should create child cursor upon replace of top level', () { var parentCursor = new NodeCursor([a]); - var childCursor = parentCursor.replaceWithAnchor('child'); + var childCursor = parentCursor.replaceWithAnchor({'k': 'v'}); - expect(parentCursor.elements.length, equals(1)); - expect(STRINGIFY(parentCursor.elements[0]), equals('')); - expect(childCursor.elements, equals([a])); + expect(parentCursor.elements.length).toEqual(1); + expect(STRINGIFY(parentCursor.elements[0])) + .toEqual(''); + expect(childCursor.elements).toEqual([a]); - var leafCursor = childCursor.replaceWithAnchor('leaf'); + var leafCursor = childCursor.replaceWithAnchor({'k2' : 'v2'}); - expect(childCursor.elements.length, equals(1)); - expect(STRINGIFY(childCursor.elements[0]), equals('')); - expect(leafCursor.elements, equals([a])); + expect(childCursor.elements.length).toEqual(1); + expect(STRINGIFY(childCursor.elements[0])) + .toEqual(''); + expect(leafCursor.elements).toEqual([a]); }); @@ -86,20 +88,22 @@ main() { var parentCursor = new NodeCursor(dom); parentCursor.descend(); // - var childCursor = parentCursor.replaceWithAnchor('child'); - expect(STRINGIFY(dom), equals('[
]')); + var childCursor = parentCursor.replaceWithAnchor({'k': 'v'}); + expect(STRINGIFY(dom)) + .toEqual('[
]'); - expect(STRINGIFY(childCursor.elements.first), equals('text')); + expect(STRINGIFY(childCursor.elements.first)).toEqual('text'); }); it('should preserve the top-level elements', () { var dom = es('textMoreText
other
'); var parentCursor = new NodeCursor(dom); - var childCursor = parentCursor.replaceWithAnchor('child'); - expect(STRINGIFY(dom), equals('[, MoreText,
other
]')); + var childCursor = parentCursor.replaceWithAnchor({'k' : 'v'}); + expect(STRINGIFY(dom)) + .toEqual('[, MoreText,
other
]'); - expect(STRINGIFY(childCursor.elements.first), equals('text')); + expect(STRINGIFY(childCursor.elements.first)).toEqual('text'); }); }); } diff --git a/test/directive/ng_repeat_spec.dart b/test/directive/ng_repeat_spec.dart index c4da255ad..6069d776f 100644 --- a/test/directive/ng_repeat_spec.dart +++ b/test/directive/ng_repeat_spec.dart @@ -410,10 +410,10 @@ main() { ''); scope.context['items'] = ['misko', 'shyam', 'frodo']; scope.apply(); - expect(element.children.length).toEqual(3); + expect(element.querySelectorAll("li").length).toEqual(3); scope.context['items'].remove('misko'); scope.apply(); - expect(element.children.length).toEqual(2); + expect(element.querySelectorAll("li").length).toEqual(2); }); it('should not error when the last watched item is removed', () { @@ -425,10 +425,10 @@ main() { ''); scope.context['items'] = ['misko', 'shyam', 'frodo']; scope.apply(); - expect(element.children.length).toEqual(3); + expect(element.querySelectorAll("li").length).toEqual(3); scope.context['items'].remove('frodo'); scope.apply(); - expect(element.children.length).toEqual(2); + expect(element.querySelectorAll("li").length).toEqual(2); }); it('should not error when multiple watched items are removed at the same time', () { @@ -440,11 +440,11 @@ main() { ''); scope.context['items'] = ['misko', 'shyam', 'frodo', 'igor']; scope.apply(); - expect(element.children.length).toEqual(4); + expect(element.querySelectorAll("li").length).toEqual(4); scope.context['items'].remove('shyam'); scope.context['items'].remove('frodo'); scope.apply(); - expect(element.children.length).toEqual(2); + expect(element.querySelectorAll("li").length).toEqual(2); }); }); diff --git a/test/directive/ng_switch_spec.dart b/test/directive/ng_switch_spec.dart index 2c26666ca..e63bbe955 100644 --- a/test/directive/ng_switch_spec.dart +++ b/test/directive/ng_switch_spec.dart @@ -3,6 +3,9 @@ library ng_switch_spec; import '../_specs.dart'; void main() { + String templates(Type type, List values) => + values.map((v) => '').join(''); + describe('ngSwitch', () { TestBed _; @@ -15,8 +18,7 @@ void main() { '
second:{{name}}
' + '
true:{{name}}
' + '
'); - expect(element.innerHtml).toEqual( - ''); + expect(element).toHaveHtml(templates(NgSwitchWhen, ['1', '2', 'true'])); _.rootScope.context['select'] = 1; _.rootScope.apply(); expect(element.text).toEqual('first:'); @@ -43,10 +45,7 @@ void main() { '
  • second:{{name}}
  • ' + '
  • true:{{name}}
  • ' + ''); - expect(element.innerHtml).toEqual('' - '' - '' - ''); + expect(element).toHaveHtml(templates(NgSwitchWhen, ['1', '1', '2', 'true'])); _.rootScope.context['select'] = 1; _.rootScope.apply(); expect(element.text).toEqual('first:, first too:');