fix(ivy): support ng-container inside another ng-container (#25346)
PR Close #25346
This commit is contained in:
parent
fefc860f35
commit
dbdbbdbe86
|
@ -145,7 +145,7 @@ function walkLNodeTree(
|
||||||
nextNode = head ? (componentHost.data as LViewData)[PARENT] ![head.index] : null;
|
nextNode = head ? (componentHost.data as LViewData)[PARENT] ![head.index] : null;
|
||||||
} else {
|
} else {
|
||||||
// Otherwise look at the first child
|
// Otherwise look at the first child
|
||||||
nextNode = getChildLNode(node as LViewNode);
|
nextNode = getChildLNode(node as LViewNode | LElementContainerNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextNode === null) {
|
if (nextNode === null) {
|
||||||
|
@ -532,6 +532,16 @@ function canInsertNativeChildOfElement(parent: LElementNode, currentView: LViewD
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We might delay insertion of children for a given view if it is disconnected.
|
||||||
|
* This might happen for 2 main reason:
|
||||||
|
* - view is not inserted into any container (view was created but not iserted yet)
|
||||||
|
* - view is inserted into a container but the container itself is not inserted into the DOM
|
||||||
|
* (container might be part of projection or child of a view that is not inserted yet).
|
||||||
|
*
|
||||||
|
* In other words we can insert children of a given view this view was inserted into a container and
|
||||||
|
* the container itself has it render parent determined.
|
||||||
|
*/
|
||||||
function canInsertNativeChildOfView(parent: LViewNode): boolean {
|
function canInsertNativeChildOfView(parent: LViewNode): boolean {
|
||||||
ngDevMode && assertNodeType(parent, TNodeType.View);
|
ngDevMode && assertNodeType(parent, TNodeType.View);
|
||||||
|
|
||||||
|
@ -635,7 +645,10 @@ export function appendChild(parent: LNode, child: RNode | null, currentView: LVi
|
||||||
nativeInsertBefore(renderer, renderParent !.native, child, beforeNode);
|
nativeInsertBefore(renderer, renderParent !.native, child, beforeNode);
|
||||||
} else if (parent.tNode.type === TNodeType.ElementContainer) {
|
} else if (parent.tNode.type === TNodeType.ElementContainer) {
|
||||||
const beforeNode = parent.native;
|
const beforeNode = parent.native;
|
||||||
const grandParent = getParentLNode(parent) as LElementNode | LViewNode;
|
let grandParent = getParentLNode(parent as LElementContainerNode);
|
||||||
|
while (grandParent.tNode.type === TNodeType.ElementContainer) {
|
||||||
|
grandParent = getParentLNode(grandParent as LElementContainerNode);
|
||||||
|
}
|
||||||
if (grandParent.tNode.type === TNodeType.View) {
|
if (grandParent.tNode.type === TNodeType.View) {
|
||||||
const renderParent = getRenderParent(grandParent as LViewNode);
|
const renderParent = getRenderParent(grandParent as LViewNode);
|
||||||
nativeInsertBefore(renderer, renderParent !.native, child, beforeNode);
|
nativeInsertBefore(renderer, renderParent !.native, child, beforeNode);
|
||||||
|
|
|
@ -651,6 +651,108 @@ describe('render3 integration test', () => {
|
||||||
expect(fixture.html).toEqual('<test-cmpt>component template</test-cmpt>');
|
expect(fixture.html).toEqual('<test-cmpt>component template</test-cmpt>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should render inside another ng-container', () => {
|
||||||
|
/**
|
||||||
|
* <ng-container>
|
||||||
|
* <ng-container>
|
||||||
|
* <ng-container>
|
||||||
|
* content
|
||||||
|
* </ng-container>
|
||||||
|
* </ng-container>
|
||||||
|
* </ng-container>
|
||||||
|
*/
|
||||||
|
const TestCmpt = createComponent('test-cmpt', function(rf: RenderFlags) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementContainerStart(0);
|
||||||
|
{
|
||||||
|
elementContainerStart(1);
|
||||||
|
{
|
||||||
|
elementContainerStart(2);
|
||||||
|
{ text(3, 'content'); }
|
||||||
|
elementContainerEnd();
|
||||||
|
}
|
||||||
|
elementContainerEnd();
|
||||||
|
}
|
||||||
|
elementContainerEnd();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function App() { element(0, 'test-cmpt'); }
|
||||||
|
|
||||||
|
const fixture = new TemplateFixture(App, () => {}, [TestCmpt]);
|
||||||
|
expect(fixture.html).toEqual('<test-cmpt>content</test-cmpt>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render inside another ng-container at the root of a delayed view', () => {
|
||||||
|
|
||||||
|
class TestDirective {
|
||||||
|
constructor(private _tplRef: TemplateRef<any>, private _vcRef: ViewContainerRef) {}
|
||||||
|
|
||||||
|
createAndInsert() { this._vcRef.insert(this._tplRef.createEmbeddedView({})); }
|
||||||
|
|
||||||
|
clear() { this._vcRef.clear(); }
|
||||||
|
|
||||||
|
static ngDirectiveDef = defineDirective({
|
||||||
|
type: TestDirective,
|
||||||
|
selectors: [['', 'testDirective', '']],
|
||||||
|
factory: () => new TestDirective(injectTemplateRef(), injectViewContainerRef()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let testDirective: TestDirective;
|
||||||
|
|
||||||
|
function embeddedTemplate(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementContainerStart(0);
|
||||||
|
{
|
||||||
|
elementContainerStart(1);
|
||||||
|
{
|
||||||
|
elementContainerStart(2);
|
||||||
|
{ text(3, 'content'); }
|
||||||
|
elementContainerEnd();
|
||||||
|
}
|
||||||
|
elementContainerEnd();
|
||||||
|
}
|
||||||
|
elementContainerEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
`<ng-template testDirective>
|
||||||
|
<ng-container>
|
||||||
|
<ng-container>
|
||||||
|
<ng-container>
|
||||||
|
content
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</ng-template>`;
|
||||||
|
const TestCmpt = createComponent('test-cmpt', function(rf: RenderFlags) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
container(0, embeddedTemplate, null, [AttributeMarker.SelectOnly, 'testDirective']);
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
testDirective = loadDirective<TestDirective>(0);
|
||||||
|
}
|
||||||
|
}, [TestDirective]);
|
||||||
|
|
||||||
|
function App() { element(0, 'test-cmpt'); }
|
||||||
|
|
||||||
|
const fixture = new ComponentFixture(TestCmpt);
|
||||||
|
expect(fixture.html).toEqual('');
|
||||||
|
|
||||||
|
testDirective !.createAndInsert();
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html).toEqual('content');
|
||||||
|
|
||||||
|
testDirective !.createAndInsert();
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html).toEqual('contentcontent');
|
||||||
|
|
||||||
|
testDirective !.clear();
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html).toEqual('');
|
||||||
|
});
|
||||||
|
|
||||||
it('should support directives and inject ElementRef', () => {
|
it('should support directives and inject ElementRef', () => {
|
||||||
|
|
||||||
class Directive {
|
class Directive {
|
||||||
|
|
Loading…
Reference in New Issue