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

Change the compiler to use template elements instead of comments for view ports #1365

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions lib/core_dom/compiler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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();
}
Expand Down Expand Up @@ -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);
Expand All @@ -156,6 +155,14 @@ class Compiler implements Function {
return viewFactory;
}

Map<String, String> _anchorAttrs(DirectiveRef directiveRef) {
return {
'type': VIEW_PORT_TYPE,
'directive': directiveRef.type.toString(),
'value' : directiveRef.value
};
}

List<TaggedElementBinder> _removeUnusedBinders(List<TaggedElementBinder> binders) {
// In order to support text nodes with directiveless parents, we
// add dummy ElementBinders to the list. After the entire template
Expand Down
6 changes: 5 additions & 1 deletion lib/core_dom/element_binder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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]";
}

Expand Down
6 changes: 4 additions & 2 deletions lib/core_dom/node_cursor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 7 additions & 3 deletions lib/core_dom/view_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -182,9 +186,9 @@ computeNodeLinkingInfos(List<dom.Node> 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;
}
Expand Down
7 changes: 4 additions & 3 deletions test/_specs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
10 changes: 8 additions & 2 deletions test/core_dom/compiler_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ forAllCompilersAndComponentFactories(fn) {
}

void main() {
String template(Type type, String value) =>
'<template type="ng/viewport" directive="$type" value="$value"></template>';

withElementProbeConfig((compilerType) =>
describe('TranscludingComponentFactory', () {
TestBed _;
Expand Down Expand Up @@ -133,9 +136,12 @@ void main() {

_.rootScope.context['items'] = [];
_.rootScope.apply();
expect(element).toHaveHtml('<!--ANCHOR: [ng-repeat]=item in items-->');

expect(element).toHaveHtml(template(NgRepeat, 'item in items'));
});



it('should compile a text child of a basic repeater', () {
var element = _.compile(
'<div ng-show="true">' +
Expand Down Expand Up @@ -174,7 +180,7 @@ void main() {

_.rootScope.context['items'] = [];
_.rootScope.apply();
expect(element).toHaveHtml('<!--ANCHOR: [ng-repeat]=item in items-->');
expect(element).toHaveHtml(template(NgRepeat, 'item in items'));
});

it('should compile text', (Compiler compile) {
Expand Down
76 changes: 40 additions & 36 deletions test/core_dom/node_cursor_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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', () {
Expand All @@ -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('<!--ANCHOR: child-->'));
expect(childCursor.elements, equals([a]));
expect(parentCursor.elements.length).toEqual(1);
expect(STRINGIFY(parentCursor.elements[0]))
.toEqual('<template class="ng-binding" k="v"></template>');
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('<!--ANCHOR: leaf-->'));
expect(leafCursor.elements, equals([a]));
expect(childCursor.elements.length).toEqual(1);
expect(STRINGIFY(childCursor.elements[0]))
.toEqual('<template class="ng-binding" k2="v2"></template>');
expect(leafCursor.elements).toEqual([a]);
});


Expand All @@ -86,20 +88,22 @@ main() {
var parentCursor = new NodeCursor(dom);
parentCursor.descend(); // <span>

var childCursor = parentCursor.replaceWithAnchor('child');
expect(STRINGIFY(dom), equals('[<div><!--ANCHOR: child--></div>]'));
var childCursor = parentCursor.replaceWithAnchor({'k': 'v'});
expect(STRINGIFY(dom))
.toEqual('[<div><template class="ng-binding" k="v"></template></div>]');

expect(STRINGIFY(childCursor.elements.first), equals('<span>text</span>'));
expect(STRINGIFY(childCursor.elements.first)).toEqual('<span>text</span>');
});

it('should preserve the top-level elements', () {
var dom = es('<span>text</span>MoreText<div>other</div>');
var parentCursor = new NodeCursor(dom);

var childCursor = parentCursor.replaceWithAnchor('child');
expect(STRINGIFY(dom), equals('[<!--ANCHOR: child-->, MoreText, <div>other</div>]'));
var childCursor = parentCursor.replaceWithAnchor({'k' : 'v'});
expect(STRINGIFY(dom))
.toEqual('[<template class="ng-binding" k="v"></template>, MoreText, <div>other</div>]');

expect(STRINGIFY(childCursor.elements.first), equals('<span>text</span>'));
expect(STRINGIFY(childCursor.elements.first)).toEqual('<span>text</span>');
});
});
}
Expand Down
12 changes: 6 additions & 6 deletions test/directive/ng_repeat_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -410,10 +410,10 @@ main() {
'</ul>');
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', () {
Expand All @@ -425,10 +425,10 @@ main() {
'</ul>');
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', () {
Expand All @@ -440,11 +440,11 @@ main() {
'</ul>');
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);
});
});

Expand Down
11 changes: 5 additions & 6 deletions test/directive/ng_switch_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ library ng_switch_spec;
import '../_specs.dart';

void main() {
String templates(Type type, List<String> values) =>
values.map((v) => '<template type="ng/viewport" directive="$type" value="$v"></template>').join('');

describe('ngSwitch', () {
TestBed _;

Expand All @@ -15,8 +18,7 @@ void main() {
'<div ng-switch-when="2">second:{{name}}</div>' +
'<div ng-switch-when="true">true:{{name}}</div>' +
'</div>');
expect(element.innerHtml).toEqual(
'<!--ANCHOR: [ng-switch-when]=1--><!--ANCHOR: [ng-switch-when]=2--><!--ANCHOR: [ng-switch-when]=true-->');
expect(element).toHaveHtml(templates(NgSwitchWhen, ['1', '2', 'true']));
_.rootScope.context['select'] = 1;
_.rootScope.apply();
expect(element.text).toEqual('first:');
Expand All @@ -43,10 +45,7 @@ void main() {
'<li ng-switch-when="2">second:{{name}}</li>' +
'<li ng-switch-when="true">true:{{name}}</li>' +
'</ul>');
expect(element.innerHtml).toEqual('<!--ANCHOR: [ng-switch-when]=1-->'
'<!--ANCHOR: [ng-switch-when]=1-->'
'<!--ANCHOR: [ng-switch-when]=2-->'
'<!--ANCHOR: [ng-switch-when]=true-->');
expect(element).toHaveHtml(templates(NgSwitchWhen, ['1', '1', '2', 'true']));
_.rootScope.context['select'] = 1;
_.rootScope.apply();
expect(element.text).toEqual('first:, first too:');
Expand Down