diff --git a/modules/angular2/src/core/render/view_factory.ts b/modules/angular2/src/core/render/view_factory.ts index da3017e9fe..d8f7ed59d4 100644 --- a/modules/angular2/src/core/render/view_factory.ts +++ b/modules/angular2/src/core/render/view_factory.ts @@ -128,13 +128,13 @@ class RenderViewBuilder implements RenderCommandVisitor { root = this.factory.createShadowRoot(el); this.nativeShadowRoots.push(root); } - this.parentStack.push(new Component(el, root, cmd, this.factory)); + var component = new Component(el, root, cmd, this.factory, this.allBuilders); + this.parentStack.push(component); return null; } visitEndComponent(context: any): any { var c = >this.parent; - var template = this.factory.resolveComponentTemplate(c.cmd.templateId); - this._visitChildTemplate(template, c, c.shadowRoot); + c.build(); this._endElement(); return null; } @@ -143,7 +143,9 @@ class RenderViewBuilder implements RenderCommandVisitor { this._addChild(el, cmd.ngContentIndex); this.boundElements.push(el); if (cmd.isMerged) { - this._visitChildTemplate(cmd.children, this.parentComponent, null); + visitAll( + new RenderViewBuilder(this.parentComponent, null, null, this.allBuilders, this.factory), + cmd.children); } return null; } @@ -172,11 +174,6 @@ class RenderViewBuilder implements RenderCommandVisitor { private _endElement() { this.parentStack.pop(); } - private _visitChildTemplate(cmds: RenderTemplateCmd[], parent: Component, rootNodesParent: N) { - visitAll(new RenderViewBuilder(parent, rootNodesParent, null, this.allBuilders, this.factory), - cmds); - } - private _addChild(node: N, ngContentIndex: number) { var parent = this.parent; if (isPresent(parent)) { @@ -194,9 +191,12 @@ class RenderViewBuilder implements RenderCommandVisitor { class Component { private contentNodesByNgContentIndex: N[][] = []; private projectingNgContentIndex: number = 0; + private viewBuilder: RenderViewBuilder; - constructor(public hostElement: N, public shadowRoot: N, public cmd: RenderBeginComponentCmd, - public factory: NodeFactory) {} + constructor(public hostElement: N, shadowRoot: N, public cmd: RenderBeginComponentCmd, + public factory: NodeFactory, allBuilders: RenderViewBuilder[]) { + this.viewBuilder = new RenderViewBuilder(this, shadowRoot, null, allBuilders, factory); + } addContentNode(ngContentIndex: number, node: N) { if (isBlank(ngContentIndex)) { if (this.cmd.nativeShadow) { @@ -215,6 +215,9 @@ class Component { this.contentNodesByNgContentIndex[ngContentIndex] : []; } + build() { + visitAll(this.viewBuilder, this.factory.resolveComponentTemplate(this.cmd.templateId)); + } } function addAll(source: any[], target: any[]) { diff --git a/modules/angular2/test/core/linker/projection_integration_spec.ts b/modules/angular2/test/core/linker/projection_integration_spec.ts index 9955c589be..e1a443d348 100644 --- a/modules/angular2/test/core/linker/projection_integration_spec.ts +++ b/modules/angular2/test/core/linker/projection_integration_spec.ts @@ -112,6 +112,26 @@ export function main() { }); })); + it('should project content components', + inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { + tcb.overrideView( + Simple, + new ViewMetadata( + {template: 'SIMPLE({{0}}||{{2}})', directives: []})) + .overrideView(OtherComp, new ViewMetadata({template: '{{1}}', directives: []})) + .overrideView(MainComp, new ViewMetadata({ + template: '', + directives: [Simple, OtherComp] + })) + .createAsync(MainComp) + .then((main) => { + + main.detectChanges(); + expect(main.debugElement.nativeElement).toHaveText('SIMPLE(0|1|2)'); + async.done(); + }); + })); + it('should not show the light dom even if there is no content tag', inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { tcb.overrideView(MainComp, @@ -453,6 +473,12 @@ class MainComp { text: string = ''; } +@Component({selector: 'other'}) +@View({template: '', directives: []}) +class OtherComp { + text: string = ''; +} + @Component({selector: 'simple', inputs: ['stringProp']}) @View({template: 'SIMPLE()', directives: []}) class Simple { diff --git a/modules/angular2/test/core/render/view_factory_spec.ts b/modules/angular2/test/core/render/view_factory_spec.ts index 7fbfd0c658..6de5731d9e 100644 --- a/modules/angular2/test/core/render/view_factory_spec.ts +++ b/modules/angular2/test/core/render/view_factory_spec.ts @@ -347,6 +347,28 @@ export function main() { .toEqual(['1.1', '1.2', '1.3', '1.4', '2.1', '2.2', '3.1', '3.1']); }); + it('should store bound elements from the view before bound elements from content components', + () => { + componentTemplates.set(0, [ + beginElement('a', ['id', '2.1'], [], true, null), + endElement(), + ]); + componentTemplates.set(1, [ + beginElement('a', ['id', '3.1'], [], true, null), + endElement(), + ]); + var view = createRenderView( + [ + beginComponent('a-comp', ['id', '1.1'], [], false, null, 0), + beginComponent('b-comp', ['id', '1.2'], [], false, null, 1), + endComponent(), + endComponent(), + ], + null, nodeFactory); + + expect(mapAttrs(view.boundElements, 'id')).toEqual(['1.1', '1.2', '2.1', '3.1']); + }); + it('should store bound text nodes after the bound text nodes of the main template', () => { componentTemplates.set(0, [ text('2.1', true, null), @@ -374,6 +396,22 @@ export function main() { }); }); + it('should store bound text nodes from the view before bound text nodes from content components', + () => { + componentTemplates.set(0, [text('2.1', true, null)]); + componentTemplates.set(1, [text('3.1', true, null)]); + var view = createRenderView( + [ + beginComponent('a-comp', [], [], false, null, 0), + beginComponent('b-comp', [], [], false, null, 1), + endComponent(), + endComponent(), + ], + null, nodeFactory); + + expect(mapText(view.boundTextNodes)).toEqual(['2.1', '3.1']); + }); + describe('content projection', () => { it('should remove non projected nodes', () => { componentTemplates.set(0, []);