diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts
index 5cd5377caa..b1ff51a1ed 100644
--- a/packages/core/src/render3/instructions.ts
+++ b/packages/core/src/render3/instructions.ts
@@ -1710,12 +1710,14 @@ export function projection(
}
if (canInsertNativeNode(currentParent, currentView)) {
+ ngDevMode && assertNodeType(currentParent, LNodeType.Element);
// process each node in the list of projected nodes:
let nodeToProject: LNode|null = node.data.head;
const lastNodeToProject = node.data.tail;
while (nodeToProject) {
appendProjectedNode(
- nodeToProject as LTextNode | LElementNode | LContainerNode, currentParent, currentView);
+ nodeToProject as LTextNode | LElementNode | LContainerNode, currentParent as LElementNode,
+ currentView);
nodeToProject = nodeToProject === lastNodeToProject ? null : nodeToProject.pNextOrParent;
}
}
diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts
index 5850a4fb5b..4aaa93d12e 100644
--- a/packages/core/src/render3/node_manipulation.ts
+++ b/packages/core/src/render3/node_manipulation.ts
@@ -473,24 +473,24 @@ export function insertChild(node: LNode, currentView: LView): void {
* @param currentView Current LView
*/
export function appendProjectedNode(
- node: LElementNode | LTextNode | LContainerNode, currentParent: LViewNode | LElementNode,
+ node: LElementNode | LTextNode | LContainerNode, currentParent: LElementNode,
currentView: LView): void {
if (node.type !== LNodeType.Container) {
appendChild(currentParent, (node as LElementNode | LTextNode).native, currentView);
- } else if (canInsertNativeNode(currentParent, currentView)) {
+ } else {
// The node we are adding is a Container and we are adding it to Element which
// is not a component (no more re-projection).
// Alternatively a container is projected at the root of a component's template
// and can't be re-projected (as not content of any component).
// Assignee the final projection location in those cases.
const lContainer = (node as LContainerNode).data;
- lContainer.renderParent = currentParent as LElementNode;
+ lContainer.renderParent = currentParent;
const views = lContainer.views;
for (let i = 0; i < views.length; i++) {
addRemoveViewFromContainer(node as LContainerNode, views[i], true, null);
}
}
if (node.dynamicLContainerNode) {
- node.dynamicLContainerNode.data.renderParent = currentParent as LElementNode;
+ node.dynamicLContainerNode.data.renderParent = currentParent;
}
}
diff --git a/packages/core/test/render3/view_container_ref_spec.ts b/packages/core/test/render3/view_container_ref_spec.ts
index 8dfb671fa7..a1b0a0fadf 100644
--- a/packages/core/test/render3/view_container_ref_spec.ts
+++ b/packages/core/test/render3/view_container_ref_spec.ts
@@ -28,6 +28,8 @@ describe('ViewContainerRef', () => {
tplRef: TemplateRef<{}>;
+ // injecting a ViewContainerRef to create a dynamic container in which embedded views will be
+ // created
constructor(public vcref: ViewContainerRef) {}
}
@@ -521,119 +523,123 @@ describe('ViewContainerRef', () => {
.toEqual('bar
');
});
- @Component({
- selector: 'child-with-selector',
- template: `
-
- `
- })
- class ChildWithSelector {
- static ngComponentDef = defineComponent({
- type: ChildWithSelector,
- selectors: [['child-with-selector']],
- factory: () => new ChildWithSelector(),
- template: (cmp: ChildWithSelector, cm: boolean) => {
- if (cm) {
- projectionDef(0, [[['header']]], ['header']);
- elementStart(1, 'first');
- { projection(2, 0, 1); }
- elementEnd();
- elementStart(3, 'second');
- { projection(4, 0); }
- elementEnd();
- }
- }
- });
- }
-
- it('should project the ViewContainerRef content along its host, when the host matches a selector',
- () => {
- @Component({
- selector: 'parent',
- template: `
-
- {{name}}
-
- `
- })
- class Parent {
- name: string = 'bar';
- static ngComponentDef = defineComponent({
- type: Parent,
- selectors: [['parent']],
- factory: () => new Parent(),
- template: (cmp: Parent, cm: boolean) => {
- if (cm) {
- container(0, embeddedTemplate);
- elementStart(1, 'child-with-selector');
- elementStart(2, 'header', ['vcref', '']);
- text(3, 'blah');
- elementEnd();
- elementEnd();
- }
- const tplRef = getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(load(0)));
- elementProperty(2, 'tplRef', bind(tplRef));
- elementProperty(2, 'name', bind(cmp.name));
- },
- directives: [ChildWithSelector, DirectiveWithVCRef]
- });
- }
-
- const fixture = new ComponentFixture(Parent);
- expect(fixture.html)
- .toEqual(
- '');
-
- directiveInstance !.vcref.createEmbeddedView(
- directiveInstance !.tplRef, fixture.component);
- fixture.update();
- expect(fixture.html)
- .toEqual(
- 'bar');
- });
-
- it('should not project the ViewContainerRef content, when the host does not match a selector', () => {
+ describe('with select', () => {
@Component({
- selector: 'parent',
+ selector: 'child-with-selector',
template: `
-
- {{name}}
-
- `
+
+ `
})
- class Parent {
- name: string = 'bar';
+ class ChildWithSelector {
static ngComponentDef = defineComponent({
- type: Parent,
- selectors: [['parent']],
- factory: () => new Parent(),
- template: (cmp: Parent, cm: boolean) => {
+ type: ChildWithSelector,
+ selectors: [['child-with-selector']],
+ factory: () => new ChildWithSelector(),
+ template: (cmp: ChildWithSelector, cm: boolean) => {
if (cm) {
- container(0, embeddedTemplate);
- elementStart(1, 'child-with-selector');
- elementStart(2, 'footer', ['vcref', '']);
- text(3, 'blah');
+ projectionDef(0, [[['header']]], ['header']);
+ elementStart(1, 'first');
+ { projection(2, 0, 1); }
elementEnd();
+ elementStart(3, 'second');
+ { projection(4, 0); }
elementEnd();
}
- const tplRef = getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(load(0)));
- elementProperty(2, 'tplRef', bind(tplRef));
- elementProperty(2, 'name', bind(cmp.name));
- },
- directives: [ChildWithSelector, DirectiveWithVCRef]
+ }
});
}
- const fixture = new ComponentFixture(Parent);
- expect(fixture.html)
- .toEqual(
- '');
+ it('should project the ViewContainerRef content along its host, when the host matches a selector',
+ () => {
+ @Component({
+ selector: 'parent',
+ template: `
+
+ {{name}}
+
+ `
+ })
+ class Parent {
+ name: string = 'bar';
+ static ngComponentDef = defineComponent({
+ type: Parent,
+ selectors: [['parent']],
+ factory: () => new Parent(),
+ template: (cmp: Parent, cm: boolean) => {
+ if (cm) {
+ container(0, embeddedTemplate);
+ elementStart(1, 'child-with-selector');
+ elementStart(2, 'header', ['vcref', '']);
+ text(3, 'blah');
+ elementEnd();
+ elementEnd();
+ }
+ const tplRef = getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(load(0)));
+ elementProperty(2, 'tplRef', bind(tplRef));
+ elementProperty(2, 'name', bind(cmp.name));
+ },
+ directives: [ChildWithSelector, DirectiveWithVCRef]
+ });
+ }
- directiveInstance !.vcref.createEmbeddedView(directiveInstance !.tplRef, fixture.component);
- fixture.update();
- expect(fixture.html)
- .toEqual(
- 'bar');
+ const fixture = new ComponentFixture(Parent);
+ expect(fixture.html)
+ .toEqual(
+ '');
+
+ directiveInstance !.vcref.createEmbeddedView(
+ directiveInstance !.tplRef, fixture.component);
+ fixture.update();
+ expect(fixture.html)
+ .toEqual(
+ 'bar');
+ });
+
+ it('should not project the ViewContainerRef content, when the host does not match a selector',
+ () => {
+ @Component({
+ selector: 'parent',
+ template: `
+
+ {{name}}
+
+ `
+ })
+ class Parent {
+ name: string = 'bar';
+ static ngComponentDef = defineComponent({
+ type: Parent,
+ selectors: [['parent']],
+ factory: () => new Parent(),
+ template: (cmp: Parent, cm: boolean) => {
+ if (cm) {
+ container(0, embeddedTemplate);
+ elementStart(1, 'child-with-selector');
+ elementStart(2, 'footer', ['vcref', '']);
+ text(3, 'blah');
+ elementEnd();
+ elementEnd();
+ }
+ const tplRef = getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(load(0)));
+ elementProperty(2, 'tplRef', bind(tplRef));
+ elementProperty(2, 'name', bind(cmp.name));
+ },
+ directives: [ChildWithSelector, DirectiveWithVCRef]
+ });
+ }
+
+ const fixture = new ComponentFixture(Parent);
+ expect(fixture.html)
+ .toEqual(
+ '');
+
+ directiveInstance !.vcref.createEmbeddedView(
+ directiveInstance !.tplRef, fixture.component);
+ fixture.update();
+ expect(fixture.html)
+ .toEqual(
+ 'bar');
+ });
});
});
});