fix(ivy): support projecting into dynamic views (#24752)
PR Close #24752
This commit is contained in:
parent
49df4ef454
commit
dc1f1295ee
|
@ -1963,12 +1963,9 @@ export function projectionDef(
|
|||
// execute selector matching logic if and only if:
|
||||
// - there are selectors defined
|
||||
// - a node has a tag name / attributes that can be matched
|
||||
if (selectors) {
|
||||
const matchedIdx = matchingSelectorIndex(componentChild.tNode, selectors, textSelectors !);
|
||||
distributedNodes[matchedIdx].push(componentChild);
|
||||
} else {
|
||||
distributedNodes[0].push(componentChild);
|
||||
}
|
||||
const bucketIndex =
|
||||
selectors ? matchingSelectorIndex(componentChild.tNode, selectors, textSelectors !) : 0;
|
||||
distributedNodes[bucketIndex].push(componentChild);
|
||||
|
||||
componentChild = getNextLNode(componentChild);
|
||||
}
|
||||
|
@ -2031,8 +2028,10 @@ export function projection(
|
|||
|
||||
const currentParent = getParentLNode(node);
|
||||
const canInsert = canInsertNativeNode(currentParent, viewData);
|
||||
let grandparent: LContainerNode;
|
||||
const renderParent = currentParent.tNode.type === TNodeType.View ?
|
||||
(getParentLNode(currentParent) as LContainerNode).data[RENDER_PARENT] ! :
|
||||
(grandparent = getParentLNode(currentParent) as LContainerNode) &&
|
||||
grandparent.data[RENDER_PARENT] ! :
|
||||
currentParent as LElementNode;
|
||||
|
||||
for (let i = 0; i < nodesForSelector.length; i++) {
|
||||
|
|
|
@ -706,6 +706,171 @@ describe('content projection', () => {
|
|||
expect(toHtml(parent)).toEqual('<child><div></div></child>');
|
||||
});
|
||||
|
||||
it('should project into dynamic views (with createEmbeddedView)', () => {
|
||||
class NgIf {
|
||||
constructor(public vcr: ViewContainerRef, public template: TemplateRef<any>) {}
|
||||
|
||||
@Input()
|
||||
set ngIf(value: boolean) {
|
||||
value ? this.vcr.createEmbeddedView(this.template) : this.vcr.clear();
|
||||
}
|
||||
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: NgIf,
|
||||
selectors: [['', 'ngIf', '']],
|
||||
inputs: {'ngIf': 'ngIf'},
|
||||
factory: () => new NgIf(injectViewContainerRef(), injectTemplateRef())
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Before-
|
||||
* <ng-template [ngIf]="showing">
|
||||
* <ng-content></ng-content>
|
||||
* </ng-template>
|
||||
* -After
|
||||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
text(1, 'Before-');
|
||||
container(2, IfTemplate, '', [AttributeMarker.SelectOnly, 'ngIf']);
|
||||
text(3, '-After');
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(2, 'ngIf', bind(ctx.showing));
|
||||
}
|
||||
|
||||
function IfTemplate(rf1: RenderFlags, ctx1: any) {
|
||||
if (rf1 & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
projection(1, 0);
|
||||
}
|
||||
}
|
||||
}, [NgIf]);
|
||||
|
||||
let child: {showing: boolean};
|
||||
/**
|
||||
* <child>
|
||||
* <div>A</div>
|
||||
* Some text
|
||||
* </child>
|
||||
*/
|
||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'child');
|
||||
{
|
||||
elementStart(1, 'div');
|
||||
{ text(2, 'A'); }
|
||||
elementEnd();
|
||||
text(3, 'Some text');
|
||||
}
|
||||
elementEnd();
|
||||
|
||||
// testing
|
||||
child = loadDirective(0);
|
||||
}
|
||||
}, [Child]);
|
||||
|
||||
const fixture = new ComponentFixture(App);
|
||||
child !.showing = true;
|
||||
fixture.update();
|
||||
expect(fixture.html).toEqual('<child>Before-<div>A</div>Some text-After</child>');
|
||||
|
||||
child !.showing = false;
|
||||
fixture.update();
|
||||
expect(fixture.html).toEqual('<child>Before--After</child>');
|
||||
|
||||
child !.showing = true;
|
||||
fixture.update();
|
||||
expect(fixture.html).toEqual('<child>Before-<div>A</div>Some text-After</child>');
|
||||
});
|
||||
|
||||
it('should project into dynamic views (with insertion)', () => {
|
||||
class NgIf {
|
||||
constructor(public vcr: ViewContainerRef, public template: TemplateRef<any>) {}
|
||||
|
||||
@Input()
|
||||
set ngIf(value: boolean) {
|
||||
if (value) {
|
||||
const viewRef = this.template.createEmbeddedView({});
|
||||
this.vcr.insert(viewRef);
|
||||
} else {
|
||||
this.vcr.clear();
|
||||
}
|
||||
}
|
||||
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: NgIf,
|
||||
selectors: [['', 'ngIf', '']],
|
||||
inputs: {'ngIf': 'ngIf'},
|
||||
factory: () => new NgIf(injectViewContainerRef(), injectTemplateRef())
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Before-
|
||||
* <ng-template [ngIf]="showing">
|
||||
* <ng-content></ng-content>
|
||||
* </ng-template>
|
||||
* -After
|
||||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
text(1, 'Before-');
|
||||
container(2, IfTemplate, '', [AttributeMarker.SelectOnly, 'ngIf']);
|
||||
text(3, '-After');
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(2, 'ngIf', bind(ctx.showing));
|
||||
}
|
||||
|
||||
function IfTemplate(rf1: RenderFlags, ctx1: any) {
|
||||
if (rf1 & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
projection(1, 0);
|
||||
}
|
||||
}
|
||||
}, [NgIf]);
|
||||
|
||||
let child: {showing: boolean};
|
||||
/**
|
||||
* <child>
|
||||
* <div>A</div>
|
||||
* Some text
|
||||
* </child>
|
||||
*/
|
||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'child');
|
||||
{
|
||||
elementStart(1, 'div');
|
||||
{ text(2, 'A'); }
|
||||
elementEnd();
|
||||
text(3, 'Some text');
|
||||
}
|
||||
elementEnd();
|
||||
|
||||
// testing
|
||||
child = loadDirective(0);
|
||||
}
|
||||
}, [Child]);
|
||||
|
||||
const fixture = new ComponentFixture(App);
|
||||
child !.showing = true;
|
||||
fixture.update();
|
||||
expect(fixture.html).toEqual('<child>Before-<div>A</div>Some text-After</child>');
|
||||
|
||||
child !.showing = false;
|
||||
fixture.update();
|
||||
expect(fixture.html).toEqual('<child>Before--After</child>');
|
||||
|
||||
child !.showing = true;
|
||||
fixture.update();
|
||||
expect(fixture.html).toEqual('<child>Before-<div>A</div>Some text-After</child>');
|
||||
});
|
||||
|
||||
it('should project nodes into the last ng-content', () => {
|
||||
/**
|
||||
* <div><ng-content></ng-content></div>
|
||||
|
|
Loading…
Reference in New Issue