fix(ivy): support ViewContainerRef on nodes projected into an embedded view (#23333)
PR Close #23333
This commit is contained in:
parent
6199ea5d4a
commit
2bb783824e
|
@ -1563,6 +1563,9 @@ export function embeddedViewEnd(): void {
|
||||||
const lContainer = containerNode.data;
|
const lContainer = containerNode.data;
|
||||||
|
|
||||||
if (creationMode) {
|
if (creationMode) {
|
||||||
|
// When projected nodes are going to be inserted, the renderParent of the dynamic container
|
||||||
|
// used by the ViewContainerRef must be set.
|
||||||
|
setRenderParentInProjectedNodes(lContainer.renderParent, viewNode);
|
||||||
// it is a new view, insert it into collection of views for a given container
|
// it is a new view, insert it into collection of views for a given container
|
||||||
insertView(containerNode, viewNode, lContainer.nextIndex);
|
insertView(containerNode, viewNode, lContainer.nextIndex);
|
||||||
}
|
}
|
||||||
|
@ -1574,6 +1577,32 @@ export function embeddedViewEnd(): void {
|
||||||
ngDevMode && assertNodeType(previousOrParentNode, LNodeType.View);
|
ngDevMode && assertNodeType(previousOrParentNode, LNodeType.View);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For nodes which are projected inside an embedded view, this function sets the renderParent
|
||||||
|
* of their dynamic LContainerNode.
|
||||||
|
* @param renderParent the renderParent of the LContainer which contains the embedded view.
|
||||||
|
* @param viewNode the embedded view.
|
||||||
|
*/
|
||||||
|
function setRenderParentInProjectedNodes(
|
||||||
|
renderParent: LElementNode | null, viewNode: LViewNode): void {
|
||||||
|
if (renderParent != null) {
|
||||||
|
let node = viewNode.child;
|
||||||
|
while (node) {
|
||||||
|
if (node.type === LNodeType.Projection) {
|
||||||
|
let nodeToProject: LNode|null = (node as LProjectionNode).data.head;
|
||||||
|
const lastNodeToProject = (node as LProjectionNode).data.tail;
|
||||||
|
while (nodeToProject) {
|
||||||
|
if (nodeToProject.dynamicLContainerNode) {
|
||||||
|
nodeToProject.dynamicLContainerNode.data.renderParent = renderParent;
|
||||||
|
}
|
||||||
|
nodeToProject = nodeToProject === lastNodeToProject ? null : nodeToProject.pNextOrParent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node = node.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/////////////
|
/////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -471,7 +471,7 @@ describe('ViewContainerRef', () => {
|
||||||
textBinding(1, ctx.name);
|
textBinding(1, ctx.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should project the ViewContainerRef content along its host', () => {
|
it('should project the ViewContainerRef content along its host, in an element', () => {
|
||||||
@Component({selector: 'child', template: '<div><ng-content></ng-content></div>'})
|
@Component({selector: 'child', template: '<div><ng-content></ng-content></div>'})
|
||||||
class Child {
|
class Child {
|
||||||
static ngComponentDef = defineComponent({
|
static ngComponentDef = defineComponent({
|
||||||
|
@ -532,6 +532,86 @@ describe('ViewContainerRef', () => {
|
||||||
.toEqual('<child><div><header vcref="">blah</header><span>bar</span></div></child>');
|
.toEqual('<child><div><header vcref="">blah</header><span>bar</span></div></child>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should project the ViewContainerRef content along its host, in a view', () => {
|
||||||
|
@Component({
|
||||||
|
selector: 'child-with-view',
|
||||||
|
template: `
|
||||||
|
% if (show) {
|
||||||
|
<ng-content></ng-content>
|
||||||
|
% }`
|
||||||
|
})
|
||||||
|
class ChildWithView {
|
||||||
|
show: boolean = true;
|
||||||
|
static ngComponentDef = defineComponent({
|
||||||
|
type: ChildWithView,
|
||||||
|
selectors: [['child-with-view']],
|
||||||
|
factory: () => new ChildWithView(),
|
||||||
|
template: (rf: RenderFlags, cmp: ChildWithView) => {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
projectionDef(0);
|
||||||
|
container(1);
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
containerRefreshStart(1);
|
||||||
|
if (cmp.show) {
|
||||||
|
let rf0 = embeddedViewStart(0);
|
||||||
|
if (rf0 & RenderFlags.Create) {
|
||||||
|
projection(0, 0);
|
||||||
|
}
|
||||||
|
embeddedViewEnd();
|
||||||
|
}
|
||||||
|
containerRefreshEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'parent',
|
||||||
|
template: `
|
||||||
|
<ng-template #foo>
|
||||||
|
<span>{{name}}</span>
|
||||||
|
</ng-template>
|
||||||
|
<child-with-view>
|
||||||
|
<header vcref [tplRef]="foo" [name]="name">blah</header>
|
||||||
|
</child-with-view>`
|
||||||
|
})
|
||||||
|
class Parent {
|
||||||
|
name: string = 'bar';
|
||||||
|
static ngComponentDef = defineComponent({
|
||||||
|
type: Parent,
|
||||||
|
selectors: [['parent']],
|
||||||
|
factory: () => new Parent(),
|
||||||
|
template: (rf: RenderFlags, cmp: Parent) => {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
container(0, embeddedTemplate);
|
||||||
|
elementStart(1, 'child-with-view');
|
||||||
|
elementStart(2, 'header', ['vcref', '']);
|
||||||
|
text(3, 'blah');
|
||||||
|
elementEnd();
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
const tplRef = getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(load(0)));
|
||||||
|
elementProperty(2, 'tplRef', bind(tplRef));
|
||||||
|
elementProperty(2, 'name', bind(cmp.name));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
directives: [ChildWithView, DirectiveWithVCRef]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const fixture = new ComponentFixture(Parent);
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual('<child-with-view><header vcref="">blah</header></child-with-view>');
|
||||||
|
|
||||||
|
directiveInstance !.vcref.createEmbeddedView(directiveInstance !.tplRef, fixture.component);
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual(
|
||||||
|
'<child-with-view><header vcref="">blah</header><span>bar</span></child-with-view>');
|
||||||
|
});
|
||||||
|
|
||||||
describe('with select', () => {
|
describe('with select', () => {
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'child-with-selector',
|
selector: 'child-with-selector',
|
||||||
|
|
Loading…
Reference in New Issue