feat(compiler): new semantics for template
attributes and view variables.
- Supports `<div template=“…”>`, including parsing the expressions within the attribute. - Supports `<template let-ng-repeat=“rows”>` - Adds attribute interpolation (was missing previously)
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
import {describe, beforeEach, it, expect, iit, ddescribe} from 'test_lib/test_lib';
|
||||
import {isPresent} from 'facade/lang';
|
||||
import {ListWrapper, MapWrapper} from 'facade/collection';
|
||||
import {ListWrapper, MapWrapper, StringMapWrapper} from 'facade/collection';
|
||||
import {DirectiveParser} from 'core/compiler/pipeline/directive_parser';
|
||||
import {CompilePipeline} from 'core/compiler/pipeline/compile_pipeline';
|
||||
import {CompileStep} from 'core/compiler/pipeline/compile_step';
|
||||
@ -12,6 +12,9 @@ import {Decorator} from 'core/annotations/decorator';
|
||||
import {Template} from 'core/annotations/template';
|
||||
import {TemplateConfig} from 'core/annotations/template_config';
|
||||
import {Reflector} from 'core/compiler/reflector';
|
||||
import {Parser} from 'change_detection/parser/parser';
|
||||
import {Lexer} from 'change_detection/parser/lexer';
|
||||
import {ClosureMap} from 'change_detection/parser/closure_map';
|
||||
|
||||
export function main() {
|
||||
describe('DirectiveParser', () => {
|
||||
@ -22,7 +25,9 @@ export function main() {
|
||||
directives = [SomeDecorator, SomeTemplate, SomeTemplate2, SomeComponent, SomeComponent2];
|
||||
});
|
||||
|
||||
function createPipeline(propertyBindings = null) {
|
||||
function createPipeline({propertyBindings, variableBindings}={}) {
|
||||
var closureMap = new ClosureMap();
|
||||
var parser = new Parser(new Lexer(), closureMap);
|
||||
var annotatedDirectives = ListWrapper.create();
|
||||
for (var i=0; i<directives.length; i++) {
|
||||
ListWrapper.push(annotatedDirectives, reflector.annotatedType(directives[i]));
|
||||
@ -30,7 +35,12 @@ export function main() {
|
||||
|
||||
return new CompilePipeline([new MockStep((parent, current, control) => {
|
||||
if (isPresent(propertyBindings)) {
|
||||
current.propertyBindings = propertyBindings;
|
||||
StringMapWrapper.forEach(propertyBindings, (v, k) => {
|
||||
current.addPropertyBinding(k, parser.parseBinding(v));
|
||||
});
|
||||
}
|
||||
if (isPresent(variableBindings)) {
|
||||
current.variableBindings = MapWrapper.createFromStringMap(variableBindings);
|
||||
}
|
||||
}), new DirectiveParser(annotatedDirectives)]);
|
||||
}
|
||||
@ -42,33 +52,26 @@ export function main() {
|
||||
expect(results[0].templateDirective).toBe(null);
|
||||
});
|
||||
|
||||
it('should detect directives in attributes', () => {
|
||||
var results = createPipeline().process(createElement('<div some-decor some-templ some-comp></div>'));
|
||||
expect(results[0].decoratorDirectives).toEqual([reflector.annotatedType(SomeDecorator)]);
|
||||
expect(results[0].templateDirective).toEqual(reflector.annotatedType(SomeTemplate));
|
||||
expect(results[0].componentDirective).toEqual(reflector.annotatedType(SomeComponent));
|
||||
});
|
||||
describe('component directives', () => {
|
||||
it('should detect them in attributes', () => {
|
||||
var results = createPipeline().process(createElement('<div some-comp></div>'));
|
||||
expect(results[0].componentDirective).toEqual(reflector.annotatedType(SomeComponent));
|
||||
});
|
||||
|
||||
it('should detect directives in property bindings', () => {
|
||||
var pipeline = createPipeline(MapWrapper.createFromStringMap({
|
||||
'some-decor': 'someExpr',
|
||||
'some-templ': 'someExpr',
|
||||
'some-comp': 'someExpr'
|
||||
}));
|
||||
var results = pipeline.process(createElement('<div></div>'));
|
||||
expect(results[0].decoratorDirectives).toEqual([reflector.annotatedType(SomeDecorator)]);
|
||||
expect(results[0].templateDirective).toEqual(reflector.annotatedType(SomeTemplate));
|
||||
expect(results[0].componentDirective).toEqual(reflector.annotatedType(SomeComponent));
|
||||
});
|
||||
it('should detect them in property bindings', () => {
|
||||
var pipeline = createPipeline({propertyBindings: {
|
||||
'some-comp': 'someExpr'
|
||||
}});
|
||||
var results = pipeline.process(createElement('<div></div>'));
|
||||
expect(results[0].componentDirective).toEqual(reflector.annotatedType(SomeComponent));
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
|
||||
it('should not allow multiple template directives on the same element', () => {
|
||||
expect( () => {
|
||||
createPipeline().process(
|
||||
createElement('<div some-templ some-templ2></div>')
|
||||
);
|
||||
}).toThrowError('Only one template directive per element is allowed!');
|
||||
it('should detect them in variable bindings', () => {
|
||||
var pipeline = createPipeline({variableBindings: {
|
||||
'some-comp': 'someExpr'
|
||||
}});
|
||||
var results = pipeline.process(createElement('<div></div>'));
|
||||
expect(results[0].componentDirective).toEqual(reflector.annotatedType(SomeComponent));
|
||||
});
|
||||
|
||||
it('should not allow multiple component directives on the same element', () => {
|
||||
@ -78,6 +81,84 @@ export function main() {
|
||||
);
|
||||
}).toThrowError('Only one component directive per element is allowed!');
|
||||
});
|
||||
|
||||
it('should not allow component directives on <template> elements', () => {
|
||||
expect( () => {
|
||||
createPipeline().process(
|
||||
createElement('<template some-comp></template>')
|
||||
);
|
||||
}).toThrowError('Only template directives are allowed on <template> elements!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('template directives', () => {
|
||||
it('should detect them in attributes', () => {
|
||||
var results = createPipeline().process(createElement('<template some-templ></template>'));
|
||||
expect(results[0].templateDirective).toEqual(reflector.annotatedType(SomeTemplate));
|
||||
});
|
||||
|
||||
it('should detect them in property bindings', () => {
|
||||
var pipeline = createPipeline({propertyBindings: {
|
||||
'some-templ': 'someExpr'
|
||||
}});
|
||||
var results = pipeline.process(createElement('<template></template>'));
|
||||
expect(results[0].templateDirective).toEqual(reflector.annotatedType(SomeTemplate));
|
||||
});
|
||||
|
||||
it('should detect them in variable bindings', () => {
|
||||
var pipeline = createPipeline({variableBindings: {
|
||||
'some-templ': 'someExpr'
|
||||
}});
|
||||
var results = pipeline.process(createElement('<template></template>'));
|
||||
expect(results[0].templateDirective).toEqual(reflector.annotatedType(SomeTemplate));
|
||||
});
|
||||
|
||||
it('should not allow multiple template directives on the same element', () => {
|
||||
expect( () => {
|
||||
createPipeline().process(
|
||||
createElement('<template some-templ some-templ2></template>')
|
||||
);
|
||||
}).toThrowError('Only one template directive per element is allowed!');
|
||||
});
|
||||
|
||||
it('should not allow template directives on non <template> elements', () => {
|
||||
expect( () => {
|
||||
createPipeline().process(
|
||||
createElement('<div some-templ></div>')
|
||||
);
|
||||
}).toThrowError('Template directives need to be placed on <template> elements or elements with template attribute!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('decorator directives', () => {
|
||||
it('should detect them in attributes', () => {
|
||||
var results = createPipeline().process(createElement('<div some-decor></div>'));
|
||||
expect(results[0].decoratorDirectives).toEqual([reflector.annotatedType(SomeDecorator)]);
|
||||
});
|
||||
|
||||
it('should detect them in property bindings', () => {
|
||||
var pipeline = createPipeline({propertyBindings: {
|
||||
'some-decor': 'someExpr'
|
||||
}});
|
||||
var results = pipeline.process(createElement('<div></div>'));
|
||||
expect(results[0].decoratorDirectives).toEqual([reflector.annotatedType(SomeDecorator)]);
|
||||
});
|
||||
|
||||
it('should detect them in variable bindings', () => {
|
||||
var pipeline = createPipeline({variableBindings: {
|
||||
'some-decor': 'someExpr'
|
||||
}});
|
||||
var results = pipeline.process(createElement('<div></div>'));
|
||||
expect(results[0].decoratorDirectives).toEqual([reflector.annotatedType(SomeDecorator)]);
|
||||
});
|
||||
|
||||
it('should not allow decorator directives on <template> elements', () => {
|
||||
expect( () => {
|
||||
createPipeline().process(
|
||||
createElement('<template some-decor></template>')
|
||||
);
|
||||
}).toThrowError('Only template directives are allowed on <template> elements!');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -31,6 +31,7 @@ export function main() {
|
||||
}={}) {
|
||||
var reflector = new Reflector();
|
||||
var closureMap = new ClosureMap();
|
||||
var parser = new Parser(new Lexer(), closureMap);
|
||||
return new CompilePipeline([
|
||||
new MockStep((parent, current, control) => {
|
||||
if (isPresent(current.element.getAttribute('viewroot'))) {
|
||||
@ -38,22 +39,24 @@ export function main() {
|
||||
current.inheritedProtoView = new ProtoView(current.element, new ProtoWatchGroup());
|
||||
} else if (isPresent(parent)) {
|
||||
current.inheritedProtoView = parent.inheritedProtoView;
|
||||
} else {
|
||||
current.inheritedProtoView = null;
|
||||
}
|
||||
var hasBinding = false;
|
||||
if (isPresent(current.element.getAttribute('text-binding'))) {
|
||||
current.textNodeBindings = textNodeBindings;
|
||||
MapWrapper.forEach(textNodeBindings, (v,k) => {
|
||||
current.addTextNodeBinding(k, parser.parseBinding(v));
|
||||
});
|
||||
hasBinding = true;
|
||||
}
|
||||
if (isPresent(current.element.getAttribute('prop-binding'))) {
|
||||
current.propertyBindings = propertyBindings;
|
||||
if (isPresent(propertyBindings)) {
|
||||
MapWrapper.forEach(propertyBindings, (v,k) => {
|
||||
current.addPropertyBinding(k, parser.parseBinding(v));
|
||||
});
|
||||
}
|
||||
hasBinding = true;
|
||||
}
|
||||
if (isPresent(protoElementInjector)) {
|
||||
current.inheritedProtoElementInjector = protoElementInjector;
|
||||
} else {
|
||||
current.inheritedProtoElementInjector = null;
|
||||
}
|
||||
if (isPresent(current.element.getAttribute('directives'))) {
|
||||
hasBinding = true;
|
||||
@ -65,7 +68,7 @@ export function main() {
|
||||
current.hasBindings = true;
|
||||
DOM.addClass(current.element, 'ng-binding');
|
||||
}
|
||||
}), new ElementBinderBuilder(new Parser(new Lexer(), closureMap), closureMap)
|
||||
}), new ElementBinderBuilder(closureMap)
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ import {Component} from 'core/annotations/component';
|
||||
export function main() {
|
||||
describe('ElementBindingMarker', () => {
|
||||
|
||||
function createPipeline({textNodeBindings, propertyBindings, directives}={}) {
|
||||
function createPipeline({textNodeBindings, propertyBindings, variableBindings, directives}={}) {
|
||||
var reflector = new Reflector();
|
||||
return new CompilePipeline([
|
||||
new MockStep((parent, current, control) => {
|
||||
@ -26,6 +26,9 @@ export function main() {
|
||||
if (isPresent(propertyBindings)) {
|
||||
current.propertyBindings = propertyBindings;
|
||||
}
|
||||
if (isPresent(variableBindings)) {
|
||||
current.variableBindings = variableBindings;
|
||||
}
|
||||
if (isPresent(directives)) {
|
||||
for (var i=0; i<directives.length; i++) {
|
||||
current.addDirective(reflector.annotatedType(directives[i]));
|
||||
@ -53,6 +56,12 @@ export function main() {
|
||||
assertBinding(results[0], true);
|
||||
});
|
||||
|
||||
it('should mark elements with variable bindings', () => {
|
||||
var variableBindings = MapWrapper.createFromStringMap({'a': 'expr'});
|
||||
var results = createPipeline({variableBindings: variableBindings}).process(createElement('<div></div>'));
|
||||
assertBinding(results[0], true);
|
||||
});
|
||||
|
||||
it('should mark elements with decorator directives', () => {
|
||||
var results = createPipeline({
|
||||
directives: [SomeDecoratorDirective]
|
||||
|
@ -4,20 +4,42 @@ import {CompilePipeline} from 'core/compiler/pipeline/compile_pipeline';
|
||||
import {DOM} from 'facade/dom';
|
||||
import {MapWrapper} from 'facade/collection';
|
||||
|
||||
import {Parser} from 'change_detection/parser/parser';
|
||||
import {ClosureMap} from 'change_detection/parser/closure_map';
|
||||
import {Lexer} from 'change_detection/parser/lexer';
|
||||
|
||||
export function main() {
|
||||
describe('PropertyBindingParser', () => {
|
||||
function createPipeline() {
|
||||
return new CompilePipeline([new PropertyBindingParser()]);
|
||||
return new CompilePipeline([new PropertyBindingParser(new Parser(new Lexer(), new ClosureMap()))]);
|
||||
}
|
||||
|
||||
it('should detect [] syntax', () => {
|
||||
var results = createPipeline().process(createElement('<div [a]="b"></div>'));
|
||||
expect(MapWrapper.get(results[0].propertyBindings, 'a')).toEqual('b');
|
||||
expect(MapWrapper.get(results[0].propertyBindings, 'a').source).toEqual('b');
|
||||
});
|
||||
|
||||
it('should detect bind- syntax', () => {
|
||||
var results = createPipeline().process(createElement('<div bind-a="b"></div>'));
|
||||
expect(MapWrapper.get(results[0].propertyBindings, 'a')).toEqual('b');
|
||||
expect(MapWrapper.get(results[0].propertyBindings, 'a').source).toEqual('b');
|
||||
});
|
||||
|
||||
it('should detect interpolation syntax', () => {
|
||||
// Note: we don't test all corner cases of interpolation as we assume shared functionality between text interpolation
|
||||
// and attribute interpolation.
|
||||
var results = createPipeline().process(createElement('<div a="{{b}}"></div>'));
|
||||
expect(MapWrapper.get(results[0].propertyBindings, 'a').source).toEqual('(b)');
|
||||
});
|
||||
|
||||
it('should detect let- syntax', () => {
|
||||
var results = createPipeline().process(createElement('<template let-a="b"></template>'));
|
||||
expect(MapWrapper.get(results[0].variableBindings, 'a')).toEqual('b');
|
||||
});
|
||||
|
||||
it('should not allow let- syntax on non template elements', () => {
|
||||
expect( () => {
|
||||
createPipeline().process(createElement('<div let-a="b"></div>'))
|
||||
}).toThrowError('let-* is only allowed on <template> elements!');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -7,14 +7,18 @@ import {CompileElement} from 'core/compiler/pipeline/compile_element';
|
||||
import {CompileStep} from 'core/compiler/pipeline/compile_step'
|
||||
import {CompileControl} from 'core/compiler/pipeline/compile_control';
|
||||
import {DOM} from 'facade/dom';
|
||||
import {MapWrapper} from 'facade/collection';
|
||||
|
||||
export function main() {
|
||||
describe('ProtoViewBuilder', () => {
|
||||
function createPipeline() {
|
||||
function createPipeline(variableBindings=null) {
|
||||
return new CompilePipeline([new MockStep((parent, current, control) => {
|
||||
if (isPresent(current.element.getAttribute('viewroot'))) {
|
||||
current.isViewRoot = true;
|
||||
}
|
||||
if (isPresent(current.element.getAttribute('var-binding'))) {
|
||||
current.variableBindings = MapWrapper.createFromStringMap(variableBindings);
|
||||
}
|
||||
current.inheritedElementBinder = new ElementBinder(null, null, null);
|
||||
}), new ProtoViewBuilder()]);
|
||||
}
|
||||
@ -37,16 +41,29 @@ export function main() {
|
||||
expect(results[1].inheritedProtoView.element).toBe(viewRootElement);
|
||||
});
|
||||
|
||||
it('should save ProtoView into elementBinder of parent element', () => {
|
||||
var el = createElement('<div viewroot><span><a viewroot></a></span></div>');
|
||||
it('should save ProtoView into the elementBinder of parent element', () => {
|
||||
var el = createElement('<div viewroot><template><a viewroot></a></template></div>');
|
||||
var results = createPipeline().process(el);
|
||||
expect(results[1].inheritedElementBinder.nestedProtoView).toBe(results[2].inheritedProtoView);
|
||||
});
|
||||
|
||||
it('should bind variables to the nested ProtoView', () => {
|
||||
var el = createElement('<div viewroot><template var-binding><a viewroot></a></template></div>');
|
||||
var results = createPipeline({
|
||||
'var1': 'map1',
|
||||
'var2': 'map2'
|
||||
}).process(el);
|
||||
var npv = results[1].inheritedElementBinder.nestedProtoView;
|
||||
expect(npv.variableBindings).toEqual(MapWrapper.createFromStringMap({
|
||||
'var1': 'map1',
|
||||
'var2': 'map2'
|
||||
}));
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
|
||||
it('should not allow multiple nested ProtoViews for the same parent element', () => {
|
||||
var el = createElement('<div viewroot><span><a viewroot></a><a viewroot></a></span></div>');
|
||||
var el = createElement('<div viewroot><template><a viewroot></a><a viewroot></a></template></div>');
|
||||
expect( () => {
|
||||
createPipeline().process(el);
|
||||
}).toThrowError('Only one nested view per element is allowed');
|
||||
|
@ -4,41 +4,45 @@ import {CompilePipeline} from 'core/compiler/pipeline/compile_pipeline';
|
||||
import {DOM} from 'facade/dom';
|
||||
import {MapWrapper} from 'facade/collection';
|
||||
|
||||
import {Parser} from 'change_detection/parser/parser';
|
||||
import {ClosureMap} from 'change_detection/parser/closure_map';
|
||||
import {Lexer} from 'change_detection/parser/lexer';
|
||||
|
||||
export function main() {
|
||||
describe('TextInterpolationParser', () => {
|
||||
function createPipeline() {
|
||||
return new CompilePipeline([new TextInterpolationParser()]);
|
||||
return new CompilePipeline([new TextInterpolationParser(new Parser(new Lexer(), new ClosureMap()))]);
|
||||
}
|
||||
|
||||
it('should find text interpolation in normal elements', () => {
|
||||
var results = createPipeline().process(createElement('<div>{{expr1}}<span></span>{{expr2}}</div>'));
|
||||
var bindings = results[0].textNodeBindings;
|
||||
expect(MapWrapper.get(bindings, 0)).toEqual("(expr1)");
|
||||
expect(MapWrapper.get(bindings, 2)).toEqual("(expr2)");
|
||||
expect(MapWrapper.get(bindings, 0).source).toEqual("(expr1)");
|
||||
expect(MapWrapper.get(bindings, 2).source).toEqual("(expr2)");
|
||||
});
|
||||
|
||||
it('should find text interpolation in template elements', () => {
|
||||
var results = createPipeline().process(createElement('<template>{{expr1}}<span></span>{{expr2}}</template>'));
|
||||
var bindings = results[0].textNodeBindings;
|
||||
expect(MapWrapper.get(bindings, 0)).toEqual("(expr1)");
|
||||
expect(MapWrapper.get(bindings, 2)).toEqual("(expr2)");
|
||||
expect(MapWrapper.get(bindings, 0).source).toEqual("(expr1)");
|
||||
expect(MapWrapper.get(bindings, 2).source).toEqual("(expr2)");
|
||||
});
|
||||
|
||||
it('should allow multiple expressions', () => {
|
||||
var results = createPipeline().process(createElement('<div>{{expr1}}{{expr2}}</div>'));
|
||||
var bindings = results[0].textNodeBindings;
|
||||
expect(MapWrapper.get(bindings, 0)).toEqual("(expr1)+(expr2)");
|
||||
expect(MapWrapper.get(bindings, 0).source).toEqual("(expr1)+(expr2)");
|
||||
});
|
||||
|
||||
it('should allow fixed text before, in between and after expressions', () => {
|
||||
var results = createPipeline().process(createElement('<div>a{{expr1}}b{{expr2}}c</div>'));
|
||||
var bindings = results[0].textNodeBindings;
|
||||
expect(MapWrapper.get(bindings, 0)).toEqual("'a'+(expr1)+'b'+(expr2)+'c'");
|
||||
expect(MapWrapper.get(bindings, 0).source).toEqual("'a'+(expr1)+'b'+(expr2)+'c'");
|
||||
});
|
||||
|
||||
it('should escape quotes in fixed parts', () => {
|
||||
var results = createPipeline().process(createElement("<div>'\"a{{expr1}}</div>"));
|
||||
expect(MapWrapper.get(results[0].textNodeBindings, 0)).toEqual("'\\'\"a'+(expr1)");
|
||||
expect(MapWrapper.get(results[0].textNodeBindings, 0).source).toEqual("'\\'\"a'+(expr1)");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -4,51 +4,35 @@ import {MapWrapper} from 'facade/collection';
|
||||
|
||||
import {ViewSplitter} from 'core/compiler/pipeline/view_splitter';
|
||||
import {CompilePipeline} from 'core/compiler/pipeline/compile_pipeline';
|
||||
import {CompileElement} from 'core/compiler/pipeline/compile_element';
|
||||
import {CompileStep} from 'core/compiler/pipeline/compile_step'
|
||||
import {CompileControl} from 'core/compiler/pipeline/compile_control';
|
||||
import {DOM, TemplateElement} from 'facade/dom';
|
||||
import {Reflector} from 'core/compiler/reflector';
|
||||
import {Template} from 'core/annotations/template';
|
||||
import {Decorator} from 'core/annotations/decorator';
|
||||
import {Component} from 'core/annotations/component';
|
||||
|
||||
import {Parser} from 'change_detection/parser/parser';
|
||||
import {ClosureMap} from 'change_detection/parser/closure_map';
|
||||
import {Lexer} from 'change_detection/parser/lexer';
|
||||
|
||||
export function main() {
|
||||
describe('ViewSplitter', () => {
|
||||
|
||||
function createPipeline({textNodeBindings, propertyBindings, directives}={}) {
|
||||
var reflector = new Reflector();
|
||||
return new CompilePipeline([
|
||||
new MockStep((parent, current, control) => {
|
||||
if (isPresent(current.element.getAttribute('tmpl'))) {
|
||||
current.addDirective(reflector.annotatedType(SomeTemplateDirective));
|
||||
if (isPresent(textNodeBindings)) {
|
||||
current.textNodeBindings = textNodeBindings;
|
||||
}
|
||||
if (isPresent(propertyBindings)) {
|
||||
current.propertyBindings = propertyBindings;
|
||||
}
|
||||
if (isPresent(directives)) {
|
||||
for (var i=0; i<directives.length; i++) {
|
||||
current.addDirective(reflector.annotatedType(directives[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}), new ViewSplitter()
|
||||
]);
|
||||
function createPipeline() {
|
||||
return new CompilePipeline([new ViewSplitter(new Parser(new Lexer(), new ClosureMap()))]);
|
||||
}
|
||||
|
||||
function commonTests(useTemplateElement) {
|
||||
var rootElement;
|
||||
beforeEach( () => {
|
||||
if (useTemplateElement) {
|
||||
rootElement = createElement('<div><span tmpl></span></div>');
|
||||
} else {
|
||||
rootElement = createElement('<div><span tmpl></span></div>');
|
||||
}
|
||||
});
|
||||
it('should mark root elements as viewRoot', () => {
|
||||
var rootElement = createElement('<div></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[0].isViewRoot).toBe(true);
|
||||
});
|
||||
|
||||
it('should mark <template> elements as viewRoot', () => {
|
||||
var rootElement = createElement('<div><template></template></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[1].isViewRoot).toBe(true);
|
||||
});
|
||||
|
||||
describe('elements with template attribute', () => {
|
||||
|
||||
it('should insert an empty <template> element', () => {
|
||||
var rootElement = createElement('<div><div template></div></div>');
|
||||
var originalChild = rootElement.childNodes[0];
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[0].element).toBe(rootElement);
|
||||
@ -57,79 +41,29 @@ export function main() {
|
||||
expect(results[2].element).toBe(originalChild);
|
||||
});
|
||||
|
||||
it('should move the template directive to the new element', () => {
|
||||
it('should mark the element as viewRoot', () => {
|
||||
var rootElement = createElement('<div><div template></div></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[1].templateDirective.type).toBe(SomeTemplateDirective);
|
||||
expect(results[2].templateDirective).toBe(null);
|
||||
});
|
||||
|
||||
it('should split the property bindings depending on the bindings on the directive', () => {
|
||||
var propertyBindings = MapWrapper.createFromStringMap({
|
||||
'templateBoundProp': 'a',
|
||||
'nonBoundProp': 'c'
|
||||
});
|
||||
var results = createPipeline({propertyBindings: propertyBindings}).process(rootElement);
|
||||
expect(MapWrapper.get(results[1].propertyBindings, 'templateBoundProp')).toEqual('a');
|
||||
expect(MapWrapper.get(results[2].propertyBindings, 'nonBoundProp')).toEqual('c');
|
||||
});
|
||||
|
||||
it('should keep the component, decorator directives and text node bindings on the original element', () => {
|
||||
var textNodeBindings = MapWrapper.create();
|
||||
MapWrapper.set(textNodeBindings, 0, 'someExpr');
|
||||
var directives = [SomeDecoratorDirective, SomeComponentDirective];
|
||||
var results = createPipeline({
|
||||
textNodeBindings: textNodeBindings,
|
||||
directives: directives
|
||||
}).process(rootElement);
|
||||
expect(results[1].componentDirective).toBe(null);
|
||||
expect(results[1].decoratorDirectives).toBe(null);
|
||||
expect(results[1].textNodeBindings).toBe(null);
|
||||
expect(results[2].componentDirective.type).toEqual(SomeComponentDirective);
|
||||
expect(results[2].decoratorDirectives[0].type).toEqual(SomeDecoratorDirective);
|
||||
expect(results[2].textNodeBindings).toEqual(textNodeBindings);
|
||||
});
|
||||
|
||||
it('should set the isViewRoot flag for the root and nested views', () => {
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[0].isViewRoot).toBe(true);
|
||||
expect(results[1].isViewRoot).toBe(false);
|
||||
expect(results[2].isViewRoot).toBe(true);
|
||||
});
|
||||
}
|
||||
|
||||
describe('template directive on normal element', () => {
|
||||
commonTests(false);
|
||||
});
|
||||
it('should add property bindings from the template attribute', () => {
|
||||
var rootElement = createElement('<div><div template="prop:expr"></div></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(MapWrapper.get(results[1].propertyBindings, 'prop').source).toEqual('expr');
|
||||
});
|
||||
|
||||
it('should add variable mappings from the template attribute', () => {
|
||||
var rootElement = createElement('<div><div template="varName #mapName"></div></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[1].variableBindings).toEqual(MapWrapper.createFromStringMap({'varName': 'mapName'}));
|
||||
});
|
||||
|
||||
describe('template directive on <template> element', () => {
|
||||
commonTests(true);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class MockStep extends CompileStep {
|
||||
constructor(process) {
|
||||
this.processClosure = process;
|
||||
}
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
this.processClosure(parent, current, control);
|
||||
}
|
||||
}
|
||||
|
||||
@Template({
|
||||
bind: {
|
||||
'templateBoundProp': 'dirProp'
|
||||
}
|
||||
})
|
||||
class SomeTemplateDirective {}
|
||||
|
||||
@Component()
|
||||
class SomeComponentDirective {}
|
||||
|
||||
@Decorator()
|
||||
class SomeDecoratorDirective {}
|
||||
|
||||
function createElement(html) {
|
||||
return DOM.createTemplate(html).content.firstChild;
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ export function main() {
|
||||
it('should collect property bindings on the root element if it has the ng-binding class', () => {
|
||||
var pv = new ProtoView(templateAwareCreateElement('<div [prop]="a" class="ng-binding"></div>'), new ProtoWatchGroup());
|
||||
pv.bindElement(null);
|
||||
pv.bindElementProperty('prop', parser.parseBinding('a'));
|
||||
pv.bindElementProperty('prop', parser.parseBinding('a').ast);
|
||||
|
||||
var view = pv.instantiate(null, null, null);
|
||||
expect(view.bindElements.length).toEqual(1);
|
||||
@ -57,7 +57,7 @@ export function main() {
|
||||
var pv = new ProtoView(templateAwareCreateElement('<div><span></span><span class="ng-binding"></span></div>'),
|
||||
new ProtoWatchGroup());
|
||||
pv.bindElement(null);
|
||||
pv.bindElementProperty('a', parser.parseBinding('b'));
|
||||
pv.bindElementProperty('a', parser.parseBinding('b').ast);
|
||||
|
||||
var view = pv.instantiate(null, null, null);
|
||||
expect(view.bindElements.length).toEqual(1);
|
||||
@ -71,8 +71,8 @@ export function main() {
|
||||
it('should collect text nodes under the root element', () => {
|
||||
var pv = new ProtoView(templateAwareCreateElement('<div class="ng-binding">{{}}<span></span>{{}}</div>'), new ProtoWatchGroup());
|
||||
pv.bindElement(null);
|
||||
pv.bindTextNode(0, parser.parseBinding('a'));
|
||||
pv.bindTextNode(2, parser.parseBinding('b'));
|
||||
pv.bindTextNode(0, parser.parseBinding('a').ast);
|
||||
pv.bindTextNode(2, parser.parseBinding('b').ast);
|
||||
|
||||
var view = pv.instantiate(null, null, null);
|
||||
expect(view.textNodes.length).toEqual(2);
|
||||
@ -84,7 +84,7 @@ export function main() {
|
||||
var pv = new ProtoView(templateAwareCreateElement('<div><span> </span><span class="ng-binding">{{}}</span></div>'),
|
||||
new ProtoWatchGroup());
|
||||
pv.bindElement(null);
|
||||
pv.bindTextNode(0, parser.parseBinding('b'));
|
||||
pv.bindTextNode(0, parser.parseBinding('b').ast);
|
||||
|
||||
var view = pv.instantiate(null, null, null);
|
||||
expect(view.textNodes.length).toEqual(1);
|
||||
@ -176,7 +176,7 @@ export function main() {
|
||||
function createComponentWithSubPV(subProtoView) {
|
||||
var pv = new ProtoView(createElement('<cmp class="ng-binding"></cmp>'), new ProtoWatchGroup());
|
||||
var binder = pv.bindElement(new ProtoElementInjector(null, 0, [SomeComponent], true));
|
||||
binder.componentDirective = someComponentDirective;
|
||||
binder.componentDirective = someComponentDirective;
|
||||
binder.nestedProtoView = subProtoView;
|
||||
return pv;
|
||||
}
|
||||
@ -239,7 +239,7 @@ export function main() {
|
||||
var pv = new ProtoView(createElement('<div class="ng-binding">{{}}</div>'),
|
||||
new ProtoWatchGroup());
|
||||
pv.bindElement(null);
|
||||
pv.bindTextNode(0, parser.parseBinding('foo'));
|
||||
pv.bindTextNode(0, parser.parseBinding('foo').ast);
|
||||
createView(pv);
|
||||
|
||||
ctx.foo = 'buz';
|
||||
@ -251,7 +251,7 @@ export function main() {
|
||||
var pv = new ProtoView(createElement('<div class="ng-binding"></div>'),
|
||||
new ProtoWatchGroup());
|
||||
pv.bindElement(null);
|
||||
pv.bindElementProperty('id', parser.parseBinding('foo'));
|
||||
pv.bindElementProperty('id', parser.parseBinding('foo').ast);
|
||||
createView(pv);
|
||||
|
||||
ctx.foo = 'buz';
|
||||
@ -263,7 +263,7 @@ export function main() {
|
||||
var pv = new ProtoView(createElement('<div class="ng-binding"></div>'),
|
||||
new ProtoWatchGroup());
|
||||
pv.bindElement(new ProtoElementInjector(null, 0, [SomeDirective]));
|
||||
pv.bindDirectiveProperty( 0, parser.parseBinding('foo'), 'prop', closureMap.setter('prop'));
|
||||
pv.bindDirectiveProperty( 0, parser.parseBinding('foo').ast, 'prop', closureMap.setter('prop'));
|
||||
createView(pv);
|
||||
|
||||
ctx.foo = 'buz';
|
||||
|
Reference in New Issue
Block a user