diff --git a/packages/core/src/render3/index.ts b/packages/core/src/render3/index.ts index 1fde6ee071..73aa3c5e77 100644 --- a/packages/core/src/render3/index.ts +++ b/packages/core/src/render3/index.ts @@ -87,3 +87,4 @@ export { defineDirective, }; export {createComponentRef, detectChanges, getHostElement, markDirty, renderComponent}; +export {CssSelector} from './interfaces/projection'; diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index bde29d5c5f..f2fd567d08 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -243,7 +243,7 @@ export function createLNode( } if (index != null) { // We are Element or Container - ngDevMode && assertEqual(data.length, index, 'data.length not in sequence'); + ngDevMode && assertDataNext(index); data[index] = node; // Every node adds a value to the static data array to avoid a sparse array @@ -1264,7 +1264,7 @@ export const componentRefresh: * * @param selectors */ -export function projectionDef(selectors?: CssSelector[]): LNode[][] { +export function projectionDef(index: number, selectors?: CssSelector[]): void { const noOfNodeBuckets = selectors ? selectors.length + 1 : 1; const distributedNodes = new Array(noOfNodeBuckets); for (let i = 0; i < noOfNodeBuckets; i++) { @@ -1305,7 +1305,8 @@ export function projectionDef(selectors?: CssSelector[]): LNode[][] { componentChild = componentChild.next; } - return distributedNodes; + ngDevMode && assertDataNext(index); + data[index] = distributedNodes; } /** @@ -1891,3 +1892,7 @@ function assertDataInRange(index: number, arr?: any[]) { if (arr == null) arr = data; assertLessThan(index, arr ? arr.length : 0, 'data.length'); } + +function assertDataNext(index: number) { + assertEqual(data.length, index, 'data.length not in sequence'); +} \ No newline at end of file diff --git a/packages/core/test/render3/compiler_canonical_spec.ts b/packages/core/test/render3/compiler_canonical_spec.ts index 37ef050769..10cae0177f 100644 --- a/packages/core/test/render3/compiler_canonical_spec.ts +++ b/packages/core/test/render3/compiler_canonical_spec.ts @@ -164,6 +164,69 @@ describe('compiler specification', () => { expect(renderComp(MyComponent)).toEqual('child-view!'); expect(log).toEqual(['ChildComponent', 'SomeDirective']); }); + + it('should support content projection', () => { + @Component({selector: 'simple', template: `
`}) + class SimpleComponent { + static ngComponentDef = r3.defineComponent({ + tag: 'simple', + factory: () => new SimpleComponent(), + template: function(ctx: SimpleComponent, cm: boolean) { + if (cm) { + r3.pD(0); + r3.E(0, 'div'); + r3.P(2, 0); + r3.e(); + } + } + }); + } + + @Component({ + selector: 'complex', + template: ` +
+
` + }) + class ComplexComponent { + static ngComponentDef = r3.defineComponent({ + tag: 'complex', + factory: () => new ComplexComponent(), + template: function(ctx: ComplexComponent, cm: boolean) { + if (cm) { + r3.pD(0, pD_0); + r3.E(1, 'div', ['id', 'first']); + r3.P(2, 0, 1); + r3.e(); + r3.E(3, 'div', ['id', 'second']); + r3.P(4, 0, 2); + r3.e(); + } + } + }); + } + const pD_0: r3.CssSelector[] = + [[[['span', 'title', 'toFirst'], null]], [[['span', 'title', 'toSecond'], null]]]; + + @Component({ + selector: 'my-app', + template: `content + ` + }) + class MyApp { + static ngComponentDef = r3.defineComponent({ + tag: 'my-app', + factory: () => new MyApp(), + template: function(ctx: MyApp, cm: boolean) { + if (cm) { + r3.E(0, SimpleComponent); + r3.T(2, 'content'); + r3.e(); + } + } + }); + } + }); }); describe('local references', () => { @@ -424,7 +487,7 @@ xdescribe('NgModule', () => { factory: () => new MyModule(inject(Toast)), provider: [ {provide: Toast, deps: [String]}, // If Toast has metadata generate this line - Toast, // If toast has not metadata generate this line. + Toast, // If Toast has no metadata generate this line. {provide: String, useValue: 'Hello'} ], imports: [CommonModule] diff --git a/packages/core/test/render3/content_spec.ts b/packages/core/test/render3/content_spec.ts index 95deea6e82..d7255db18b 100644 --- a/packages/core/test/render3/content_spec.ts +++ b/packages/core/test/render3/content_spec.ts @@ -18,7 +18,7 @@ describe('content projection', () => { */ const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { - m(0, pD()); + pD(0); E(1, 'div'); { P(2, 0); } e(); @@ -44,7 +44,7 @@ describe('content projection', () => { it('should project content when root.', () => { const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { - m(0, pD()); + pD(0); P(1, 0); } }); @@ -64,7 +64,7 @@ describe('content projection', () => { it('should re-project content when root.', () => { const GrandChild = createComponent('grand-child', function(ctx: any, cm: boolean) { if (cm) { - m(0, pD()); + pD(0); E(1, 'div'); { P(2, 0); } e(); @@ -72,7 +72,7 @@ describe('content projection', () => { }); const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { - m(0, pD()); + pD(0); E(1, GrandChild); { P(3, 0); } e(); @@ -102,7 +102,7 @@ describe('content projection', () => { it('should project content with container.', () => { const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { - m(0, pD()); + pD(0); E(1, 'div'); { P(2, 0); } e(); @@ -144,7 +144,7 @@ describe('content projection', () => { it('should project content with container into root', () => { const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { - m(0, pD()); + pD(0); P(1, 0); } }); @@ -182,7 +182,7 @@ describe('content projection', () => { it('should project content with container and if-else.', () => { const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { - m(0, pD()); + pD(0); E(1, 'div'); { P(2, 0); } e(); @@ -240,7 +240,7 @@ describe('content projection', () => { */ const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { - m(0, pD()); + pD(0); E(1, 'div'); { C(2); } e(); @@ -295,7 +295,7 @@ describe('content projection', () => { */ const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { - m(0, pD()); + pD(0); E(1, 'div'); { C(2); } e(); @@ -342,7 +342,7 @@ describe('content projection', () => { */ const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { - m(0, pD()); + pD(0); E(1, 'div'); { P(2, 0); } e(); @@ -389,7 +389,7 @@ describe('content projection', () => { */ const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { - m(0, pD()); + pD(0); P(1, 0); E(2, 'div'); { C(3); } @@ -439,8 +439,7 @@ describe('content projection', () => { */ const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { - m(0, - pD([[[['span', 'title', 'toFirst'], null]], [[['span', 'title', 'toSecond'], null]]])); + pD(0, [[[['span', 'title', 'toFirst'], null]], [[['span', 'title', 'toSecond'], null]]]); E(1, 'div', ['id', 'first']); { P(2, 0, 1); } e(); @@ -486,8 +485,7 @@ describe('content projection', () => { */ const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { - m(0, - pD([[[['span', 'class', 'toFirst'], null]], [[['span', 'class', 'toSecond'], null]]])); + pD(0, [[[['span', 'class', 'toFirst'], null]], [[['span', 'class', 'toSecond'], null]]]); E(1, 'div', ['id', 'first']); { P(2, 0, 1); } e(); @@ -533,8 +531,7 @@ describe('content projection', () => { */ const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { - m(0, - pD([[[['span', 'class', 'toFirst'], null]], [[['span', 'class', 'toSecond'], null]]])); + pD(0, [[[['span', 'class', 'toFirst'], null]], [[['span', 'class', 'toSecond'], null]]]); E(1, 'div', ['id', 'first']); { P(2, 0, 1); } e(); @@ -580,7 +577,7 @@ describe('content projection', () => { */ const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { - m(0, pD([[[['span'], null]], [[['span', 'class', 'toSecond'], null]]])); + pD(0, [[[['span'], null]], [[['span', 'class', 'toSecond'], null]]]); E(1, 'div', ['id', 'first']); { P(2, 0, 1); } e(); @@ -626,7 +623,7 @@ describe('content projection', () => { */ const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { - m(0, pD([[[['span', 'class', 'toFirst'], null]]])); + pD(0, [[[['span', 'class', 'toFirst'], null]]]); E(1, 'div', ['id', 'first']); { P(2, 0, 1); } e(); @@ -673,7 +670,7 @@ describe('content projection', () => { */ const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { - m(0, pD([[[['span', 'class', 'toSecond'], null]]])); + pD(0, [[[['span', 'class', 'toSecond'], null]]]); E(1, 'div', ['id', 'first']); { P(2, 0); } e(); @@ -727,7 +724,7 @@ describe('content projection', () => { */ const GrandChild = createComponent('grand-child', function(ctx: any, cm: boolean) { if (cm) { - m(0, pD([[[['span'], null]]])); + pD(0, [[[['span'], null]]]); P(1, 0, 1); E(2, 'hr'); e(); @@ -743,7 +740,7 @@ describe('content projection', () => { */ const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { - m(0, pD()); + pD(0); E(1, GrandChild); { P(3, 0); @@ -793,7 +790,7 @@ describe('content projection', () => { */ const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { - m(0, pD([[[['div'], null]]])); + pD(0, [[[['div'], null]]]); E(1, 'span'); { P(2, 0, 1); } e();