feat(Compiler): Multiple template per component
fixes #596 - TemplateConfig becomes Template - introduce a TemplateResolver to pick the cmp template, - @Component and @Template are disociated
This commit is contained in:
263
modules/angular2/test/core/compiler/compiler_spec.js
vendored
263
modules/angular2/test/core/compiler/compiler_spec.js
vendored
@ -1,7 +1,8 @@
|
||||
import {describe, beforeEach, it, expect, ddescribe, iit, el, IS_DARTIUM} from 'angular2/test_lib';
|
||||
|
||||
import {DOM, Element, TemplateElement} from 'angular2/src/facade/dom';
|
||||
import {List, ListWrapper, Map, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {Type, isBlank, stringify} from 'angular2/src/facade/lang';
|
||||
import {Type, isBlank, stringify, isPresent} from 'angular2/src/facade/lang';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
||||
@ -9,39 +10,39 @@ import {ProtoView} from 'angular2/src/core/compiler/view';
|
||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
|
||||
import {Component} from 'angular2/src/core/annotations/annotations';
|
||||
import {TemplateConfig} from 'angular2/src/core/annotations/template_config';
|
||||
import {Template} from 'angular2/src/core/annotations/template';
|
||||
import {CompileElement} from 'angular2/src/core/compiler/pipeline/compile_element';
|
||||
import {CompileStep} from 'angular2/src/core/compiler/pipeline/compile_step'
|
||||
import {CompileControl} from 'angular2/src/core/compiler/pipeline/compile_control';
|
||||
import {TemplateLoader} from 'angular2/src/core/compiler/template_loader';
|
||||
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
|
||||
|
||||
import {Lexer, Parser, dynamicChangeDetection} from 'angular2/change_detection';
|
||||
import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
||||
import {XHRMock} from 'angular2/src/mock/xhr_mock';
|
||||
|
||||
export function main() {
|
||||
describe('compiler', function() {
|
||||
var reader;
|
||||
|
||||
beforeEach( () => {
|
||||
reader = new DirectiveMetadataReader();
|
||||
});
|
||||
|
||||
var syncTemplateLoader = new FakeTemplateLoader();
|
||||
syncTemplateLoader.forceSync();
|
||||
var asyncTemplateLoader = new FakeTemplateLoader();
|
||||
asyncTemplateLoader.forceAsync();
|
||||
|
||||
StringMapWrapper.forEach({
|
||||
'(sync TemplateLoader)': syncTemplateLoader,
|
||||
'(async TemplateLoader)': asyncTemplateLoader
|
||||
}, (templateLoader, name) => {
|
||||
'(sync TemplateLoader)': true,
|
||||
'(async TemplateLoader)': false
|
||||
}, (sync, name) => {
|
||||
var reader, tplResolver;
|
||||
|
||||
beforeEach(() => {
|
||||
reader = new DirectiveMetadataReader();
|
||||
tplResolver = new FakeTemplateResolver();
|
||||
if (sync) {
|
||||
tplResolver.forceSync();
|
||||
} else {
|
||||
tplResolver.forceAsync();
|
||||
}
|
||||
});
|
||||
|
||||
describe(name, () => {
|
||||
|
||||
function createCompiler(processClosure) {
|
||||
var steps = [new MockStep(processClosure)];
|
||||
return new TestableCompiler(reader, steps, templateLoader);
|
||||
return new TestableCompiler(reader, steps, new FakeTemplateLoader(), tplResolver);
|
||||
}
|
||||
|
||||
it('should run the steps and return the ProtoView of the root element', (done) => {
|
||||
@ -49,43 +50,35 @@ export function main() {
|
||||
var compiler = createCompiler( (parent, current, control) => {
|
||||
current.inheritedProtoView = rootProtoView;
|
||||
});
|
||||
compiler.compile(MainComponent, el('<div></div>')).then( (protoView) => {
|
||||
tplResolver.setTemplate(MainComponent, new Template({inline: '<div></div>'}));
|
||||
compiler.compile(MainComponent).then( (protoView) => {
|
||||
expect(protoView).toBe(rootProtoView);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should use the given element', (done) => {
|
||||
var element = el('<div></div>');
|
||||
it('should use the inline template', (done) => {
|
||||
var compiler = createCompiler( (parent, current, control) => {
|
||||
current.inheritedProtoView = new ProtoView(current.element, null, null);
|
||||
});
|
||||
compiler.compile(MainComponent, element).then( (protoView) => {
|
||||
expect(protoView.element).toBe(element);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should use the inline template if no element is given explicitly', (done) => {
|
||||
var compiler = createCompiler( (parent, current, control) => {
|
||||
current.inheritedProtoView = new ProtoView(current.element, null, null);
|
||||
});
|
||||
compiler.compile(MainComponent, null).then( (protoView) => {
|
||||
compiler.compile(MainComponent).then( (protoView) => {
|
||||
expect(DOM.getInnerHTML(protoView.element)).toEqual('inline component');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should load nested components', (done) => {
|
||||
var mainEl = el('<div></div>');
|
||||
var compiler = createCompiler( (parent, current, control) => {
|
||||
current.inheritedProtoView = new ProtoView(current.element, null, null);
|
||||
current.inheritedElementBinder = current.inheritedProtoView.bindElement(null);
|
||||
if (current.element === mainEl) {
|
||||
if (DOM.hasClass(current.element, 'nested')) {
|
||||
current.componentDirective = reader.read(NestedComponent);
|
||||
current.inheritedProtoView = parent.inheritedProtoView;
|
||||
current.inheritedElementBinder = current.inheritedProtoView.bindElement(null);
|
||||
} else {
|
||||
current.inheritedProtoView = new ProtoView(current.element, null, null);
|
||||
}
|
||||
});
|
||||
compiler.compile(MainComponent, mainEl).then( (protoView) => {
|
||||
tplResolver.setTemplate(MainComponent, new Template({inline: '<div class="nested"></div>'}));
|
||||
compiler.compile(MainComponent).then( (protoView) => {
|
||||
var nestedView = protoView.elementBinders[0].nestedProtoView;
|
||||
expect(DOM.getInnerHTML(nestedView.element)).toEqual('nested component');
|
||||
done();
|
||||
@ -93,14 +86,14 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should cache compiled components', (done) => {
|
||||
var element = el('<div></div>');
|
||||
var compiler = createCompiler( (parent, current, control) => {
|
||||
current.inheritedProtoView = new ProtoView(current.element, null, null);
|
||||
});
|
||||
var firstProtoView;
|
||||
compiler.compile(MainComponent, element).then( (protoView) => {
|
||||
tplResolver.setTemplate(MainComponent, new Template({inline: '<div></div>'}));
|
||||
compiler.compile(MainComponent).then( (protoView) => {
|
||||
firstProtoView = protoView;
|
||||
return compiler.compile(MainComponent, element);
|
||||
return compiler.compile(MainComponent);
|
||||
}).then( (protoView) => {
|
||||
expect(firstProtoView).toBe(protoView);
|
||||
done();
|
||||
@ -109,7 +102,6 @@ export function main() {
|
||||
|
||||
it('should re-use components being compiled', (done) => {
|
||||
var nestedElBinders = [];
|
||||
var mainEl = el('<div><div class="nested"></div><div class="nested"></div></div>');
|
||||
var compiler = createCompiler( (parent, current, control) => {
|
||||
if (DOM.hasClass(current.element, 'nested')) {
|
||||
current.inheritedProtoView = new ProtoView(current.element, null, null);
|
||||
@ -118,7 +110,9 @@ export function main() {
|
||||
ListWrapper.push(nestedElBinders, current.inheritedElementBinder);
|
||||
}
|
||||
});
|
||||
compiler.compile(MainComponent, mainEl).then( (protoView) => {
|
||||
tplResolver.setTemplate(MainComponent,
|
||||
new Template({inline: '<div><div class="nested"></div><div class="nested"></div></div>'}));
|
||||
compiler.compile(MainComponent).then( (protoView) => {
|
||||
expect(nestedElBinders[0].nestedProtoView).toBe(nestedElBinders[1].nestedProtoView);
|
||||
done();
|
||||
});
|
||||
@ -130,7 +124,7 @@ export function main() {
|
||||
current.inheritedElementBinder = current.inheritedProtoView.bindElement(null);
|
||||
current.componentDirective = reader.read(RecursiveComponent);
|
||||
});
|
||||
compiler.compile(RecursiveComponent, null).then( (protoView) => {
|
||||
compiler.compile(RecursiveComponent).then( (protoView) => {
|
||||
expect(protoView.elementBinders[0].nestedProtoView).toBe(protoView);
|
||||
done();
|
||||
});
|
||||
@ -139,12 +133,14 @@ export function main() {
|
||||
});
|
||||
|
||||
describe('(mixed async, sync TemplateLoader)', () => {
|
||||
function createCompiler(processClosure, templateLoader: TemplateLoader) {
|
||||
var reader = new DirectiveMetadataReader();
|
||||
|
||||
function createCompiler(processClosure, resolver: TemplateResolver) {
|
||||
var steps = [new MockStep(processClosure)];
|
||||
return new TestableCompiler(reader, steps, templateLoader);
|
||||
return new TestableCompiler(reader, steps, new FakeTemplateLoader(), resolver);
|
||||
}
|
||||
|
||||
function createNestedComponentSpec(name, loader: TemplateLoader, error:string = null) {
|
||||
function createNestedComponentSpec(name, resolver: TemplateResolver, error:string = null) {
|
||||
it(`should load nested components ${name}`, (done) => {
|
||||
|
||||
var compiler = createCompiler((parent, current, control) => {
|
||||
@ -155,7 +151,7 @@ export function main() {
|
||||
} else {
|
||||
current.inheritedProtoView = new ProtoView(current.element, null, null);
|
||||
}
|
||||
}, loader);
|
||||
}, resolver);
|
||||
|
||||
PromiseWrapper.then(compiler.compile(ParentComponent),
|
||||
function(protoView) {
|
||||
@ -172,89 +168,77 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
var loader = new FakeTemplateLoader();
|
||||
loader.setSync(ParentComponent);
|
||||
loader.setSync(NestedComponent);
|
||||
createNestedComponentSpec('(sync -> sync)', loader);
|
||||
var resolver = new FakeTemplateResolver();
|
||||
resolver.setSync(ParentComponent);
|
||||
resolver.setSync(NestedComponent);
|
||||
createNestedComponentSpec('(sync -> sync)', resolver);
|
||||
|
||||
loader = new FakeTemplateLoader();
|
||||
loader.setAsync(ParentComponent);
|
||||
loader.setSync(NestedComponent);
|
||||
createNestedComponentSpec('(async -> sync)', loader);
|
||||
resolver = new FakeTemplateResolver();
|
||||
resolver.setAsync(ParentComponent);
|
||||
resolver.setSync(NestedComponent);
|
||||
createNestedComponentSpec('(async -> sync)', resolver);
|
||||
|
||||
loader = new FakeTemplateLoader();
|
||||
loader.setSync(ParentComponent);
|
||||
loader.setAsync(NestedComponent);
|
||||
createNestedComponentSpec('(sync -> async)', loader);
|
||||
resolver = new FakeTemplateResolver();
|
||||
resolver.setSync(ParentComponent);
|
||||
resolver.setAsync(NestedComponent);
|
||||
createNestedComponentSpec('(sync -> async)', resolver);
|
||||
|
||||
loader = new FakeTemplateLoader();
|
||||
loader.setAsync(ParentComponent);
|
||||
loader.setAsync(NestedComponent);
|
||||
createNestedComponentSpec('(async -> async)', loader);
|
||||
resolver = new FakeTemplateResolver();
|
||||
resolver.setAsync(ParentComponent);
|
||||
resolver.setAsync(NestedComponent);
|
||||
createNestedComponentSpec('(async -> async)', resolver);
|
||||
|
||||
loader = new FakeTemplateLoader();
|
||||
loader.setError(ParentComponent);
|
||||
loader.setSync(NestedComponent);
|
||||
createNestedComponentSpec('(error -> sync)', loader,
|
||||
resolver = new FakeTemplateResolver();
|
||||
resolver.setError(ParentComponent);
|
||||
resolver.setSync(NestedComponent);
|
||||
createNestedComponentSpec('(error -> sync)', resolver,
|
||||
'Failed to load the template for ParentComponent');
|
||||
|
||||
// TODO(vicb): Check why errors this fails with Dart
|
||||
// TODO(vicb): The Promise is rejected with the correct error but an exc is thrown before
|
||||
//loader = new FakeTemplateLoader();
|
||||
//loader.setSync(ParentComponent);
|
||||
//loader.setError(NestedComponent);
|
||||
//createNestedComponentSpec('(sync -> error)', loader,
|
||||
//resolver = new FakeTemplateResolver();
|
||||
//resolver.setSync(ParentComponent);
|
||||
//resolver.setError(NestedComponent);
|
||||
//createNestedComponentSpec('(sync -> error)', resolver,
|
||||
// 'Failed to load the template for NestedComponent -> Failed to compile ParentComponent');
|
||||
//
|
||||
//loader = new FakeTemplateLoader();
|
||||
//loader.setAsync(ParentComponent);
|
||||
//loader.setError(NestedComponent);
|
||||
//createNestedComponentSpec('(async -> error)', loader,
|
||||
//resolver = new FakeTemplateResolver();
|
||||
//resolver.setAsync(ParentComponent);
|
||||
//resolver.setError(NestedComponent);
|
||||
//createNestedComponentSpec('(async -> error)', resolver,
|
||||
// 'Failed to load the template for NestedComponent -> Failed to compile ParentComponent');
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: new TemplateConfig({
|
||||
inline: '<div class="parent"></div>'
|
||||
})
|
||||
})
|
||||
@Component()
|
||||
@Template({inline: '<div class="parent"></div>'})
|
||||
class ParentComponent {}
|
||||
|
||||
@Component({
|
||||
template: new TemplateConfig({
|
||||
inline: 'inline component'
|
||||
})
|
||||
})
|
||||
@Component()
|
||||
@Template({inline: 'inline component'})
|
||||
class MainComponent {}
|
||||
|
||||
@Component({
|
||||
template: new TemplateConfig({
|
||||
inline: 'nested component'
|
||||
})
|
||||
})
|
||||
@Component()
|
||||
@Template({inline: 'nested component'})
|
||||
class NestedComponent {}
|
||||
|
||||
@Component({
|
||||
template: new TemplateConfig({
|
||||
inline: '<div rec-comp></div>'
|
||||
}),
|
||||
selector: 'rec-comp'
|
||||
})
|
||||
@Component({selector: 'rec-comp'})
|
||||
@Template({inline: '<div rec-comp></div>'})
|
||||
class RecursiveComponent {}
|
||||
|
||||
class TestableCompiler extends Compiler {
|
||||
steps:List;
|
||||
|
||||
constructor(reader:DirectiveMetadataReader, steps:List<CompileStep>, loader: TemplateLoader) {
|
||||
constructor(reader:DirectiveMetadataReader, steps:List<CompileStep>, loader: TemplateLoader,
|
||||
resolver: TemplateResolver) {
|
||||
super(dynamicChangeDetection, loader, reader, new Parser(new Lexer()), new CompilerCache(),
|
||||
new NativeShadowDomStrategy());
|
||||
new NativeShadowDomStrategy(), resolver);
|
||||
this.steps = steps;
|
||||
}
|
||||
|
||||
createSteps(component):List<CompileStep> {
|
||||
createSteps(component:Type, template: Template):List<CompileStep> {
|
||||
return this.steps;
|
||||
}
|
||||
}
|
||||
@ -271,19 +255,70 @@ class MockStep extends CompileStep {
|
||||
}
|
||||
|
||||
class FakeTemplateLoader extends TemplateLoader {
|
||||
constructor() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
load(template: Template) {
|
||||
if (isPresent(template.inline)) {
|
||||
return DOM.createTemplate(template.inline);
|
||||
}
|
||||
|
||||
if (isPresent(template.url)) {
|
||||
var tplElement = DOM.createTemplate(template.url);
|
||||
return PromiseWrapper.resolve(tplElement);
|
||||
}
|
||||
|
||||
return PromiseWrapper.reject('Fail to load');
|
||||
}
|
||||
}
|
||||
|
||||
class FakeTemplateResolver extends TemplateResolver {
|
||||
_forceSync: boolean;
|
||||
_forceAsync: boolean;
|
||||
_cmpTemplates: Map;
|
||||
_syncCmp: List<Type>;
|
||||
_asyncCmp: List<Type>;
|
||||
_errorCmp: List<Type>;
|
||||
|
||||
constructor() {
|
||||
super (new XHRMock());
|
||||
super();
|
||||
this._forceSync = false;
|
||||
this._forceAsync = false;
|
||||
this._syncCmp = [];
|
||||
this._asyncCmp = [];
|
||||
this._errorCmp = [];
|
||||
this._cmpTemplates = MapWrapper.create();
|
||||
}
|
||||
|
||||
resolve(component: Type): Template {
|
||||
var template = MapWrapper.get(this._cmpTemplates, component);
|
||||
if (isBlank(template)) {
|
||||
template = super.resolve(component);
|
||||
}
|
||||
|
||||
var html = template.inline;
|
||||
|
||||
if (isBlank(template.inline)) {
|
||||
throw 'The tested component must define an inline template';
|
||||
}
|
||||
|
||||
if (ListWrapper.contains(this._errorCmp, component)) {
|
||||
return new Template({url: null, inline: null});
|
||||
}
|
||||
|
||||
if (ListWrapper.contains(this._syncCmp, component)) {
|
||||
return new Template({inline: html});
|
||||
}
|
||||
|
||||
if (ListWrapper.contains(this._asyncCmp, component)) {
|
||||
return new Template({url: html});
|
||||
}
|
||||
|
||||
if (this._forceSync) return new Template({inline: html});
|
||||
if (this._forceAsync) return new Template({url: html});
|
||||
|
||||
throw 'No template';
|
||||
}
|
||||
|
||||
forceSync() {
|
||||
@ -308,31 +343,7 @@ class FakeTemplateLoader extends TemplateLoader {
|
||||
ListWrapper.push(this._errorCmp, component);
|
||||
}
|
||||
|
||||
load(cmpMetadata: DirectiveMetadata) {
|
||||
var annotation:Component = cmpMetadata.annotation;
|
||||
var tplConfig:TemplateConfig = annotation.template;
|
||||
|
||||
if (isBlank(tplConfig.inline)) {
|
||||
throw 'The component must define an inline template';
|
||||
}
|
||||
|
||||
var template = DOM.createTemplate(tplConfig.inline);
|
||||
|
||||
if (ListWrapper.contains(this._errorCmp, cmpMetadata.type)) {
|
||||
return PromiseWrapper.reject('Fail to load');
|
||||
}
|
||||
|
||||
if (ListWrapper.contains(this._syncCmp, cmpMetadata.type)) {
|
||||
return template;
|
||||
}
|
||||
|
||||
if (ListWrapper.contains(this._asyncCmp, cmpMetadata.type)) {
|
||||
return PromiseWrapper.resolve(template);
|
||||
}
|
||||
|
||||
if (this._forceSync) return template;
|
||||
if (this._forceAsync) return PromiseWrapper.resolve(template);
|
||||
|
||||
throw `No template configured for ${stringify(cmpMetadata.type)}`;
|
||||
setTemplate(component: Type, template: Template) {
|
||||
MapWrapper.set(this._cmpTemplates, component, template);
|
||||
}
|
||||
}
|
||||
|
@ -1,43 +1,25 @@
|
||||
import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/test_lib';
|
||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||
import {Decorator, Component} from 'angular2/src/core/annotations/annotations';
|
||||
import {TemplateConfig} from 'angular2/src/core/annotations/template_config';
|
||||
import {Decorator, Component, Viewport} from 'angular2/src/core/annotations/annotations';
|
||||
import {Template} from 'angular2/src/core/annotations/template';
|
||||
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
|
||||
import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
||||
import {CONST} from 'angular2/src/facade/lang';
|
||||
import {If, Foreach} from 'angular2/directives';
|
||||
|
||||
|
||||
@Decorator({
|
||||
selector: 'someSelector'
|
||||
})
|
||||
class SomeDirective {
|
||||
}
|
||||
@Decorator({selector: 'someDecorator'})
|
||||
class SomeDecorator {}
|
||||
|
||||
@Component({selector: 'someComponent'})
|
||||
class SomeComponent {}
|
||||
|
||||
@Viewport({selector: 'someViewport'})
|
||||
class SomeViewport {}
|
||||
|
||||
class SomeDirectiveWithoutAnnotation {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'withoutDirectives'
|
||||
})
|
||||
class ComponentWithoutDirectives {}
|
||||
|
||||
@Component({
|
||||
selector: 'withDirectives',
|
||||
template: new TemplateConfig({
|
||||
directives: [ComponentWithoutDirectives]
|
||||
})
|
||||
})
|
||||
class ComponentWithDirectives {}
|
||||
|
||||
@Component({
|
||||
selector: 'withDirectivesTree',
|
||||
template: new TemplateConfig({
|
||||
directives: [[SomeDirective, [Foreach, If]], ComponentWithoutDirectives]
|
||||
})
|
||||
})
|
||||
class ComponentWithDirectivesTree {}
|
||||
|
||||
export function main() {
|
||||
describe("DirectiveMetadataReader", () => {
|
||||
var reader;
|
||||
@ -46,10 +28,22 @@ export function main() {
|
||||
reader = new DirectiveMetadataReader();
|
||||
});
|
||||
|
||||
it('should read out the annotation', () => {
|
||||
var directiveMetadata = reader.read(SomeDirective);
|
||||
it('should read out the Decorator annotation', () => {
|
||||
var directiveMetadata = reader.read(SomeDecorator);
|
||||
expect(directiveMetadata).toEqual(
|
||||
new DirectiveMetadata(SomeDirective, new Decorator({selector: 'someSelector'}), null));
|
||||
new DirectiveMetadata(SomeDecorator, new Decorator({selector: 'someDecorator'})));
|
||||
});
|
||||
|
||||
it('should read out the Viewport annotation', () => {
|
||||
var directiveMetadata = reader.read(SomeViewport);
|
||||
expect(directiveMetadata).toEqual(
|
||||
new DirectiveMetadata(SomeViewport, new Viewport({selector: 'someViewport'})));
|
||||
});
|
||||
|
||||
it('should read out the Component annotation', () => {
|
||||
var directiveMetadata = reader.read(SomeComponent);
|
||||
expect(directiveMetadata).toEqual(
|
||||
new DirectiveMetadata(SomeComponent, new Component({selector: 'someComponent'})));
|
||||
});
|
||||
|
||||
it('should throw if not matching annotation is found', () => {
|
||||
@ -57,22 +51,5 @@ export function main() {
|
||||
reader.read(SomeDirectiveWithoutAnnotation);
|
||||
}).toThrowError('No Directive annotation found on SomeDirectiveWithoutAnnotation');
|
||||
});
|
||||
|
||||
describe("componentDirectives", () => {
|
||||
it("should return an empty list when no directives specified", () => {
|
||||
var cmp = reader.read(ComponentWithoutDirectives);
|
||||
expect(cmp.componentDirectives).toEqual([]);
|
||||
});
|
||||
|
||||
it("should return a list of directives specified in the template config", () => {
|
||||
var cmp = reader.read(ComponentWithDirectives);
|
||||
expect(cmp.componentDirectives).toEqual([ComponentWithoutDirectives]);
|
||||
});
|
||||
|
||||
it("should return a list of directives specified in the template config as a tree", () => {
|
||||
var cmp = reader.read(ComponentWithDirectivesTree);
|
||||
expect(cmp.componentDirectives).toEqual([SomeDirective, Foreach, If, ComponentWithoutDirectives]);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import {describe, xit, it, expect, beforeEach, ddescribe, iit, el} from 'angular2/test_lib';
|
||||
|
||||
import {DOM} from 'angular2/src/facade/dom';
|
||||
import {Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {Type, isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
import {Injector} from 'angular2/di';
|
||||
import {Lexer, Parser, ChangeDetector, dynamicChangeDetection} from 'angular2/change_detection';
|
||||
@ -9,27 +11,27 @@ import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||
import {NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
||||
import {TemplateLoader} from 'angular2/src/core/compiler/template_loader';
|
||||
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
|
||||
import {BindingPropagationConfig} from 'angular2/src/core/compiler/binding_propagation_config';
|
||||
|
||||
import {Decorator, Component, Viewport} from 'angular2/src/core/annotations/annotations';
|
||||
import {TemplateConfig} from 'angular2/src/core/annotations/template_config';
|
||||
import {Template} from 'angular2/src/core/annotations/template';
|
||||
|
||||
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {XHRMock} from 'angular2/src/mock/xhr_mock';
|
||||
|
||||
export function main() {
|
||||
describe('integration tests', function() {
|
||||
var compiler;
|
||||
var compiler, tplResolver;
|
||||
|
||||
beforeEach( () => {
|
||||
tplResolver = new FakeTemplateResolver();
|
||||
compiler = new Compiler(dynamicChangeDetection,
|
||||
new TemplateLoader(new XHRMock()),
|
||||
new TemplateLoader(null),
|
||||
new DirectiveMetadataReader(),
|
||||
new Parser(new Lexer()),
|
||||
new CompilerCache(),
|
||||
new NativeShadowDomStrategy()
|
||||
new NativeShadowDomStrategy(),
|
||||
tplResolver
|
||||
);
|
||||
});
|
||||
|
||||
@ -43,7 +45,9 @@ export function main() {
|
||||
}
|
||||
|
||||
it('should consume text node changes', (done) => {
|
||||
compiler.compile(MyComp, el('<div>{{ctxProp}}</div>')).then((pv) => {
|
||||
tplResolver.setTemplate(MyComp, new Template({inline: '<div>{{ctxProp}}</div>'}));
|
||||
|
||||
compiler.compile(MyComp).then((pv) => {
|
||||
createView(pv);
|
||||
ctx.ctxProp = 'Hello World!';
|
||||
|
||||
@ -54,7 +58,9 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should consume element binding changes', (done) => {
|
||||
compiler.compile(MyComp, el('<div [id]="ctxProp"></div>')).then((pv) => {
|
||||
tplResolver.setTemplate(MyComp, new Template({inline: '<div [id]="ctxProp"></div>'}));
|
||||
|
||||
compiler.compile(MyComp).then((pv) => {
|
||||
createView(pv);
|
||||
|
||||
ctx.ctxProp = 'Hello World!';
|
||||
@ -73,7 +79,9 @@ export function main() {
|
||||
'<div my-dir elprop="Hi {{\'there!\'}}"></div>' +
|
||||
'<div my-dir elprop="One more {{ctxProp}}"></div>' +
|
||||
'</div>'
|
||||
compiler.compile(MyComp, el(tpl)).then((pv) => {
|
||||
tplResolver.setTemplate(MyComp, new Template({inline: tpl, directives: [MyDir]}));
|
||||
|
||||
compiler.compile(MyComp).then((pv) => {
|
||||
createView(pv);
|
||||
|
||||
ctx.ctxProp = 'Hello World!';
|
||||
@ -88,7 +96,12 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should support nested components.', (done) => {
|
||||
compiler.compile(MyComp, el('<child-cmp></child-cmp>')).then((pv) => {
|
||||
tplResolver.setTemplate(MyComp, new Template({
|
||||
inline: '<child-cmp></child-cmp>',
|
||||
directives: [ChildComp]
|
||||
}));
|
||||
|
||||
compiler.compile(MyComp).then((pv) => {
|
||||
createView(pv);
|
||||
|
||||
cd.detectChanges();
|
||||
@ -100,7 +113,13 @@ export function main() {
|
||||
|
||||
// GH issue 328 - https://github.com/angular/angular/issues/328
|
||||
it('should support different directive types on a single node', (done) => {
|
||||
compiler.compile(MyComp, el('<child-cmp my-dir [elprop]="ctxProp"></child-cmp>')).then((pv) => {
|
||||
tplResolver.setTemplate(MyComp,
|
||||
new Template({
|
||||
inline: '<child-cmp my-dir [elprop]="ctxProp"></child-cmp>',
|
||||
directives: [MyDir, ChildComp]
|
||||
}));
|
||||
|
||||
compiler.compile(MyComp).then((pv) => {
|
||||
createView(pv);
|
||||
|
||||
ctx.ctxProp = 'Hello World!';
|
||||
@ -115,7 +134,13 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should support template directives via `<template>` elements.', (done) => {
|
||||
compiler.compile(MyComp, el('<div><template some-tmplate var-greeting="some-tmpl"><copy-me>{{greeting}}</copy-me></template></div>')).then((pv) => {
|
||||
tplResolver.setTemplate(MyComp,
|
||||
new Template({
|
||||
inline: '<div><template some-viewport var-greeting="some-tmpl"><copy-me>{{greeting}}</copy-me></template></div>',
|
||||
directives: [SomeViewport]
|
||||
}));
|
||||
|
||||
compiler.compile(MyComp).then((pv) => {
|
||||
createView(pv);
|
||||
|
||||
cd.detectChanges();
|
||||
@ -130,7 +155,12 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should support template directives via `template` attribute.', (done) => {
|
||||
compiler.compile(MyComp, el('<div><copy-me template="some-tmplate: var greeting=some-tmpl">{{greeting}}</copy-me></div>')).then((pv) => {
|
||||
tplResolver.setTemplate(MyComp, new Template({
|
||||
inline: '<div><copy-me template="some-viewport: var greeting=some-tmpl">{{greeting}}</copy-me></div>',
|
||||
directives: [SomeViewport]
|
||||
}));
|
||||
|
||||
compiler.compile(MyComp).then((pv) => {
|
||||
createView(pv);
|
||||
|
||||
cd.detectChanges();
|
||||
@ -145,7 +175,12 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should assign the component instance to a var-', (done) => {
|
||||
compiler.compile(MyComp, el('<p><child-cmp var-alice></child-cmp></p>')).then((pv) => {
|
||||
tplResolver.setTemplate(MyComp, new Template({
|
||||
inline: '<p><child-cmp var-alice></child-cmp></p>',
|
||||
directives: [ChildComp]
|
||||
}));
|
||||
|
||||
compiler.compile(MyComp).then((pv) => {
|
||||
createView(pv);
|
||||
|
||||
expect(view.contextWithLocals).not.toBe(null);
|
||||
@ -156,9 +191,12 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should assign two component instances each with a var-', (done) => {
|
||||
var element = el('<p><child-cmp var-alice></child-cmp><child-cmp var-bob></p>');
|
||||
tplResolver.setTemplate(MyComp, new Template({
|
||||
inline: '<p><child-cmp var-alice></child-cmp><child-cmp var-bob></p>',
|
||||
directives: [ChildComp]
|
||||
}));
|
||||
|
||||
compiler.compile(MyComp, element).then((pv) => {
|
||||
compiler.compile(MyComp).then((pv) => {
|
||||
createView(pv);
|
||||
|
||||
expect(view.contextWithLocals).not.toBe(null);
|
||||
@ -171,7 +209,12 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should assign the component instance to a var- with shorthand syntax', (done) => {
|
||||
compiler.compile(MyComp, el('<child-cmp #alice></child-cmp>')).then((pv) => {
|
||||
tplResolver.setTemplate(MyComp, new Template({
|
||||
inline: '<child-cmp #alice></child-cmp>',
|
||||
directives: [ChildComp]
|
||||
}));
|
||||
|
||||
compiler.compile(MyComp).then((pv) => {
|
||||
createView(pv);
|
||||
|
||||
expect(view.contextWithLocals).not.toBe(null);
|
||||
@ -182,13 +225,10 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should assign the element instance to a user-defined variable', (done) => {
|
||||
// How is this supposed to work?
|
||||
var element = el('<p></p>');
|
||||
var div = el('<div var-alice></div>');
|
||||
DOM.appendChild(div, el('<i>Hello</i>'));
|
||||
DOM.appendChild(element, div);
|
||||
tplResolver.setTemplate(MyComp,
|
||||
new Template({inline: '<p><div var-alice><i>Hello</i></div></p>'}));
|
||||
|
||||
compiler.compile(MyComp, element).then((pv) => {
|
||||
compiler.compile(MyComp).then((pv) => {
|
||||
createView(pv);
|
||||
expect(view.contextWithLocals).not.toBe(null);
|
||||
|
||||
@ -201,7 +241,12 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should provide binding configuration config to the component', (done) => {
|
||||
compiler.compile(MyComp, el('<push-cmp #cmp></push-cmp>')).then((pv) => {
|
||||
tplResolver.setTemplate(MyComp, new Template({
|
||||
inline: '<push-cmp #cmp></push-cmp>',
|
||||
directives: [[[PushBasedComp]]]
|
||||
}));
|
||||
|
||||
compiler.compile(MyComp).then((pv) => {
|
||||
createView(pv);
|
||||
|
||||
var cmp = view.contextWithLocals.get('cmp');
|
||||
@ -234,12 +279,8 @@ class MyDir {
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'push-cmp',
|
||||
template: new TemplateConfig({
|
||||
inline: '{{field}}'
|
||||
})
|
||||
})
|
||||
@Component({selector: 'push-cmp'})
|
||||
@Template({inline: '{{field}}'})
|
||||
class PushBasedComp {
|
||||
numberOfChecks:number;
|
||||
bpc:BindingPropagationConfig;
|
||||
@ -260,11 +301,7 @@ class PushBasedComp {
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: new TemplateConfig({
|
||||
directives: [MyDir, [[ChildComp], SomeViewport, PushBasedComp]]
|
||||
})
|
||||
})
|
||||
@Component()
|
||||
class MyComp {
|
||||
ctxProp:string;
|
||||
constructor() {
|
||||
@ -274,11 +311,11 @@ class MyComp {
|
||||
|
||||
@Component({
|
||||
selector: 'child-cmp',
|
||||
componentServices: [MyService],
|
||||
template: new TemplateConfig({
|
||||
directives: [MyDir],
|
||||
inline: '{{ctxProp}}'
|
||||
})
|
||||
componentServices: [MyService]
|
||||
})
|
||||
@Template({
|
||||
directives: [MyDir],
|
||||
inline: '{{ctxProp}}'
|
||||
})
|
||||
class ChildComp {
|
||||
ctxProp:string;
|
||||
@ -290,7 +327,7 @@ class ChildComp {
|
||||
}
|
||||
|
||||
@Viewport({
|
||||
selector: '[some-tmplate]'
|
||||
selector: '[some-viewport]'
|
||||
})
|
||||
class SomeViewport {
|
||||
constructor(container: ViewContainer) {
|
||||
@ -305,3 +342,26 @@ class MyService {
|
||||
this.greeting = 'hello';
|
||||
}
|
||||
}
|
||||
|
||||
class FakeTemplateResolver extends TemplateResolver {
|
||||
_cmpTemplates: Map;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._cmpTemplates = MapWrapper.create();
|
||||
}
|
||||
|
||||
setTemplate(component: Type, template: Template) {
|
||||
MapWrapper.set(this._cmpTemplates, component, template);
|
||||
}
|
||||
|
||||
resolve(component: Type): Template {
|
||||
var override = MapWrapper.get(this._cmpTemplates, component);
|
||||
|
||||
if (isPresent(override)) {
|
||||
return override;
|
||||
}
|
||||
|
||||
return super.resolve(component);
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import {CompileControl} from 'angular2/src/core/compiler/pipeline/compile_contro
|
||||
import {DOM} from 'angular2/src/facade/dom';
|
||||
import {NativeShadowDomStrategy, ShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
||||
import {Component, Decorator, Viewport} from 'angular2/src/core/annotations/annotations';
|
||||
import {TemplateConfig} from 'angular2/src/core/annotations/template_config';
|
||||
import {Template} from 'angular2/src/core/annotations/template';
|
||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||
import {Lexer, Parser} from 'angular2/change_detection';
|
||||
|
||||
@ -235,19 +235,14 @@ class SomeViewport {}
|
||||
})
|
||||
class SomeViewport2 {}
|
||||
|
||||
@Component({
|
||||
selector: '[some-comp]'
|
||||
})
|
||||
@Component({selector: '[some-comp]'})
|
||||
class SomeComponent {}
|
||||
|
||||
@Component({
|
||||
selector: '[some-comp2]'
|
||||
})
|
||||
@Component({selector: '[some-comp2]'})
|
||||
class SomeComponent2 {}
|
||||
|
||||
@Component({
|
||||
template: new TemplateConfig({
|
||||
@Component()
|
||||
@Template({
|
||||
directives: [SomeDecorator, SomeViewport, SomeViewport2, SomeComponent, SomeComponent2]
|
||||
})
|
||||
})
|
||||
class MyComp {}
|
||||
|
@ -405,9 +405,7 @@ class SomeViewportDirectiveWithBinding {
|
||||
class SomeComponentDirective {
|
||||
}
|
||||
|
||||
@Component({
|
||||
bind: {'boundprop3': 'compProp'}
|
||||
})
|
||||
@Component({bind: {'boundprop3': 'compProp'}})
|
||||
class SomeComponentDirectiveWithBinding {
|
||||
compProp;
|
||||
constructor() {
|
||||
|
@ -14,7 +14,7 @@ export function main() {
|
||||
describe('ShadowDomTransformer', () => {
|
||||
function createPipeline(selector, strategy:ShadowDomStrategy, styleHost) {
|
||||
var component = new Component({selector: selector});
|
||||
var meta = new DirectiveMetadata(null, component, null);
|
||||
var meta = new DirectiveMetadata(null, component);
|
||||
var transformer = new ShadowDomTransformer(meta, strategy, styleHost);
|
||||
transformer.clearCache();
|
||||
return new CompilePipeline([transformer]);
|
||||
|
@ -1,6 +1,8 @@
|
||||
import {describe, xit, it, expect, beforeEach, ddescribe, iit, el} from 'angular2/test_lib';
|
||||
|
||||
import {DOM} from 'angular2/src/facade/dom';
|
||||
import {StringMapWrapper, MapWrapper, List} from 'angular2/src/facade/collection';
|
||||
import {isPresent, Type} from 'angular2/src/facade/lang';
|
||||
|
||||
import {Injector} from 'angular2/di';
|
||||
import {Lexer, Parser, ChangeDetector, dynamicChangeDetection} from 'angular2/change_detection';
|
||||
@ -12,14 +14,12 @@ import {ShadowDomStrategy,
|
||||
NativeShadowDomStrategy,
|
||||
EmulatedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
||||
import {TemplateLoader} from 'angular2/src/core/compiler/template_loader';
|
||||
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
|
||||
|
||||
import {Decorator, Component, Viewport} from 'angular2/src/core/annotations/annotations';
|
||||
import {TemplateConfig} from 'angular2/src/core/annotations/template_config';
|
||||
import {Template} from 'angular2/src/core/annotations/template';
|
||||
|
||||
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
||||
import {StringMapWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {XHRMock} from 'angular2/src/mock/xhr_mock';
|
||||
|
||||
export function main() {
|
||||
describe('integration tests', function() {
|
||||
@ -31,22 +31,28 @@ export function main() {
|
||||
(strategy, name) => {
|
||||
|
||||
describe(`${name} shadow dom strategy`, () => {
|
||||
var compiler;
|
||||
var compiler, tplResolver;
|
||||
|
||||
beforeEach( () => {
|
||||
beforeEach(() => {
|
||||
tplResolver = new FakeTemplateResolver();
|
||||
compiler = new Compiler(dynamicChangeDetection,
|
||||
new TemplateLoader(new XHRMock()),
|
||||
new TemplateLoader(null),
|
||||
new DirectiveMetadataReader(),
|
||||
new Parser(new Lexer()),
|
||||
new CompilerCache(),
|
||||
strategy
|
||||
strategy,
|
||||
tplResolver
|
||||
);
|
||||
});
|
||||
|
||||
function compile(template, assertions) {
|
||||
compiler.compile(MyComp, el(template)).
|
||||
then(createView).
|
||||
then((view) => {
|
||||
function compile(template, directives: List<Type>, assertions) {
|
||||
tplResolver.setTemplate(MyComp, new Template({
|
||||
inline: template,
|
||||
directives: directives
|
||||
}));
|
||||
compiler.compile(MyComp)
|
||||
.then(createView)
|
||||
.then((view) => {
|
||||
var lc = new LifeCycle(view.changeDetector, false);
|
||||
assertions(view, lc);
|
||||
});
|
||||
@ -59,7 +65,7 @@ export function main() {
|
||||
'<div class="left">A</div>' +
|
||||
'</multiple-content-tags>';
|
||||
|
||||
compile(temp, (view, lc) => {
|
||||
compile(temp, [MultipleContentTagsComponent], (view, lc) => {
|
||||
expect(view.nodes).toHaveText('(A, BC)');
|
||||
done();
|
||||
});
|
||||
@ -71,7 +77,7 @@ export function main() {
|
||||
'<div>C</div>' +
|
||||
'</multiple-content-tags>';
|
||||
|
||||
compile(temp, (view, lc) => {
|
||||
compile(temp, [MultipleContentTagsComponent], (view, lc) => {
|
||||
expect(view.nodes).toHaveText('(, BAC)');
|
||||
done();
|
||||
});
|
||||
@ -83,7 +89,7 @@ export function main() {
|
||||
'<div>B</div>' +
|
||||
'</multiple-content-tags>';
|
||||
|
||||
compile(temp, (view, lc) => {
|
||||
compile(temp, [MultipleContentTagsComponent, ManualViewportDirective], (view, lc) => {
|
||||
var dir = view.elementInjectors[1].get(ManualViewportDirective);
|
||||
|
||||
expect(view.nodes).toHaveText('(, B)');
|
||||
@ -108,7 +114,7 @@ export function main() {
|
||||
'<div>B</div>' +
|
||||
'</multiple-content-tags>';
|
||||
|
||||
compile(temp, (view, lc) => {
|
||||
compile(temp, [MultipleContentTagsComponent, ManualViewportDirective], (view, lc) => {
|
||||
var dir = view.elementInjectors[1].get(ManualViewportDirective);
|
||||
|
||||
expect(view.nodes).toHaveText('(, B)');
|
||||
@ -133,7 +139,7 @@ export function main() {
|
||||
'<div>B</div>' +
|
||||
'</outer-with-indirect-nested>';
|
||||
|
||||
compile(temp, (view, lc) => {
|
||||
compile(temp, [OuterWithIndirectNestedComponent], (view, lc) => {
|
||||
expect(view.nodes).toHaveText('OUTER(SIMPLE(AB))');
|
||||
|
||||
done();
|
||||
@ -147,7 +153,7 @@ export function main() {
|
||||
'<div>C</div>' +
|
||||
'</outer>';
|
||||
|
||||
compile(temp, (view, lc) => {
|
||||
compile(temp, [OuterComponent, ManualViewportDirective], (view, lc) => {
|
||||
var dir = view.elementInjectors[1].get(ManualViewportDirective);
|
||||
|
||||
expect(view.nodes).toHaveText('OUTER(INNER(INNERINNER(,BC)))');
|
||||
@ -257,31 +263,23 @@ class AutoViewportDirective {
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'simple',
|
||||
template: new TemplateConfig({
|
||||
inline: 'SIMPLE(<content></content>)'
|
||||
})
|
||||
})
|
||||
@Component({selector: 'simple'})
|
||||
@Template({inline: 'SIMPLE(<content></content>)'})
|
||||
class Simple {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'multiple-content-tags',
|
||||
template: new TemplateConfig({
|
||||
inline: '(<content select=".left"></content>, <content></content>)'
|
||||
})
|
||||
@Component({selector: 'multiple-content-tags'})
|
||||
@Template({
|
||||
inline: '(<content select=".left"></content>, <content></content>)'
|
||||
})
|
||||
class MultipleContentTagsComponent {
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'conditional-content',
|
||||
template: new TemplateConfig({
|
||||
inline: '<div>(<div template="auto: cond"><content select=".left"></content></div>, <content></content>)</div>',
|
||||
directives: [AutoViewportDirective]
|
||||
})
|
||||
@Component({selector: 'conditional-content'})
|
||||
@Template({
|
||||
inline: '<div>(<div template="auto: cond"><content select=".left"></content></div>, <content></content>)</div>',
|
||||
directives: [AutoViewportDirective]
|
||||
})
|
||||
class ConditionalContentComponent {
|
||||
cond:boolean;
|
||||
@ -294,52 +292,42 @@ class ConditionalContentComponent {
|
||||
hideLeft() { this.cond = false; }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'outer-with-indirect-nested',
|
||||
template: new TemplateConfig({
|
||||
inline: 'OUTER(<simple><div><content></content></div></simple>)',
|
||||
directives: [Simple]
|
||||
})
|
||||
@Component({selector: 'outer-with-indirect-nested'})
|
||||
@Template({
|
||||
inline: 'OUTER(<simple><div><content></content></div></simple>)',
|
||||
directives: [Simple]
|
||||
})
|
||||
class OuterWithIndirectNestedComponent {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'outer',
|
||||
template: new TemplateConfig({
|
||||
inline: 'OUTER(<inner><content></content></inner>)',
|
||||
directives: [InnerComponent]
|
||||
})
|
||||
@Component({selector: 'outer'})
|
||||
@Template({
|
||||
inline: 'OUTER(<inner><content></content></inner>)',
|
||||
directives: [InnerComponent]
|
||||
})
|
||||
class OuterComponent {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'inner',
|
||||
template: new TemplateConfig({
|
||||
inline: 'INNER(<innerinner><content></content></innerinner>)',
|
||||
directives: [InnerInnerComponent]
|
||||
})
|
||||
@Component({selector: 'inner'})
|
||||
@Template({
|
||||
inline: 'INNER(<innerinner><content></content></innerinner>)',
|
||||
directives: [InnerInnerComponent]
|
||||
})
|
||||
class InnerComponent {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'innerinner',
|
||||
template: new TemplateConfig({
|
||||
inline: 'INNERINNER(<content select=".left"></content>,<content></content>)'
|
||||
})
|
||||
@Component({selector: 'innerinner'})
|
||||
@Template({
|
||||
inline: 'INNERINNER(<content select=".left"></content>,<content></content>)'
|
||||
})
|
||||
class InnerInnerComponent {
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: new TemplateConfig({
|
||||
directives: [MultipleContentTagsComponent, ManualViewportDirective,
|
||||
ConditionalContentComponent, OuterWithIndirectNestedComponent, OuterComponent]
|
||||
})
|
||||
@Component({selector: 'my-comp'})
|
||||
@Template({
|
||||
directives: [MultipleContentTagsComponent, ManualViewportDirective,
|
||||
ConditionalContentComponent, OuterWithIndirectNestedComponent, OuterComponent]
|
||||
})
|
||||
class MyComp {
|
||||
}
|
||||
@ -349,3 +337,26 @@ function createView(pv) {
|
||||
view.hydrate(new Injector([]), null, {});
|
||||
return view;
|
||||
}
|
||||
|
||||
class FakeTemplateResolver extends TemplateResolver {
|
||||
_cmpTemplates: Map;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._cmpTemplates = MapWrapper.create();
|
||||
}
|
||||
|
||||
setTemplate(component: Type, template: Template) {
|
||||
MapWrapper.set(this._cmpTemplates, component, template);
|
||||
}
|
||||
|
||||
resolve(component: Type): Template {
|
||||
var override = MapWrapper.get(this._cmpTemplates, component);
|
||||
|
||||
if (isPresent(override)) {
|
||||
return override;
|
||||
}
|
||||
|
||||
return super.resolve(component);
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,15 @@
|
||||
import {describe, it, expect, beforeEach, ddescribe, iit, xit, el} from 'angular2/test_lib';
|
||||
|
||||
import {TemplateLoader} from 'angular2/src/core/compiler/template_loader';
|
||||
import {Component} from 'angular2/src/core/annotations/annotations';
|
||||
import {TemplateConfig} from 'angular2/src/core/annotations/template_config';
|
||||
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
|
||||
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
|
||||
|
||||
import {Component} from 'angular2/src/core/annotations/annotations';
|
||||
import {Template} from 'angular2/src/core/annotations/template';
|
||||
|
||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {Type, stringify, isPresent} from 'angular2/src/facade/lang';
|
||||
import {Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {XHRMock} from 'angular2/src/mock/xhr_mock';
|
||||
|
||||
@ -17,25 +22,16 @@ export function main() {
|
||||
loader = new TemplateLoader(xhr);
|
||||
});
|
||||
|
||||
function createMetadata({inline = null, url = null}={}) {
|
||||
var config = new TemplateConfig({url: url, inline: inline});
|
||||
var component = new Component({template: config});
|
||||
return new DirectiveMetadata(FakeComponent, component, null);
|
||||
}
|
||||
|
||||
it('should load inline templates synchronously', () => {
|
||||
var template = 'inline template';
|
||||
var md = createMetadata({inline: template});
|
||||
expect(loader.load(md).content).toHaveText(template);
|
||||
var template = new Template({inline: 'inline template'});
|
||||
expect(loader.load(template).content).toHaveText('inline template');
|
||||
});
|
||||
|
||||
it('should load templates through XHR', (done) => {
|
||||
var url = '/foo';
|
||||
var template = 'xhr template';
|
||||
xhr.expect(url, template);
|
||||
var md = createMetadata({url: '/foo'});
|
||||
loader.load(md).then((el) => {
|
||||
expect(el.content).toHaveText(template);
|
||||
xhr.expect('/foo', 'xhr template');
|
||||
var template = new Template({url: '/foo'});
|
||||
loader.load(template).then((el) => {
|
||||
expect(el.content).toHaveText('xhr template');
|
||||
done();
|
||||
});
|
||||
xhr.flush();
|
||||
@ -43,34 +39,31 @@ export function main() {
|
||||
|
||||
it('should cache template loaded through XHR', (done) => {
|
||||
var firstEl;
|
||||
var url = '/foo';
|
||||
var template = 'xhr template';
|
||||
xhr.expect(url, template);
|
||||
var md = createMetadata({url: '/foo'});
|
||||
loader.load(md)
|
||||
xhr.expect('/foo', 'xhr template');
|
||||
var template = new Template({url: '/foo'});
|
||||
loader.load(template)
|
||||
.then((el) => {
|
||||
firstEl = el;
|
||||
return loader.load(md);
|
||||
return loader.load(template);
|
||||
})
|
||||
.then((el) =>{
|
||||
expect(el).toBe(firstEl);
|
||||
expect(el.content).toHaveText(template);
|
||||
expect(el.content).toHaveText('xhr template');
|
||||
done();
|
||||
});
|
||||
xhr.flush();
|
||||
});
|
||||
|
||||
it('should throw when no template is defined', () => {
|
||||
var md = createMetadata();
|
||||
expect(() => loader.load(md))
|
||||
.toThrowError('No template configured for component FakeComponent');
|
||||
var template = new Template({inline: null, url: null});
|
||||
expect(() => loader.load(template))
|
||||
.toThrowError('Templates should have either their url or inline property set');
|
||||
});
|
||||
|
||||
it('should return a rejected Promise when xhr loading fails', (done) => {
|
||||
var url = '/foo';
|
||||
xhr.expect(url, null);
|
||||
var md = createMetadata({url: '/foo'});
|
||||
PromiseWrapper.then(loader.load(md),
|
||||
xhr.expect('/foo', null);
|
||||
var template = new Template({url: '/foo'});
|
||||
PromiseWrapper.then(loader.load(template),
|
||||
function(_) { throw 'Unexpected response'; },
|
||||
function(error) {
|
||||
expect(error).toEqual('Failed to load /foo');
|
||||
@ -83,5 +76,5 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
class FakeComponent {
|
||||
class SomeComponent {
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_meta
|
||||
import {Component, Decorator, Viewport, Directive, onChange} from 'angular2/src/core/annotations/annotations';
|
||||
import {Lexer, Parser, DynamicProtoChangeDetector,
|
||||
ChangeDetector} from 'angular2/change_detection';
|
||||
import {TemplateConfig} from 'angular2/src/core/annotations/template_config';
|
||||
import {Template} from 'angular2/src/core/annotations/template';
|
||||
import {EventEmitter} from 'angular2/src/core/annotations/events';
|
||||
import {List, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {DOM, Element} from 'angular2/src/facade/dom';
|
||||
@ -649,9 +649,7 @@ class DirectiveImplementingOnChange {
|
||||
|
||||
class SomeService {}
|
||||
|
||||
@Component({
|
||||
componentServices: [SomeService]
|
||||
})
|
||||
@Component({componentServices: [SomeService]})
|
||||
class SomeComponent {
|
||||
service: SomeService;
|
||||
constructor(service: SomeService) {
|
||||
|
Reference in New Issue
Block a user