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:
|
// execute selector matching logic if and only if:
|
||||||
// - there are selectors defined
|
// - there are selectors defined
|
||||||
// - a node has a tag name / attributes that can be matched
|
// - a node has a tag name / attributes that can be matched
|
||||||
if (selectors) {
|
const bucketIndex =
|
||||||
const matchedIdx = matchingSelectorIndex(componentChild.tNode, selectors, textSelectors !);
|
selectors ? matchingSelectorIndex(componentChild.tNode, selectors, textSelectors !) : 0;
|
||||||
distributedNodes[matchedIdx].push(componentChild);
|
distributedNodes[bucketIndex].push(componentChild);
|
||||||
} else {
|
|
||||||
distributedNodes[0].push(componentChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentChild = getNextLNode(componentChild);
|
componentChild = getNextLNode(componentChild);
|
||||||
}
|
}
|
||||||
@ -2031,8 +2028,10 @@ export function projection(
|
|||||||
|
|
||||||
const currentParent = getParentLNode(node);
|
const currentParent = getParentLNode(node);
|
||||||
const canInsert = canInsertNativeNode(currentParent, viewData);
|
const canInsert = canInsertNativeNode(currentParent, viewData);
|
||||||
|
let grandparent: LContainerNode;
|
||||||
const renderParent = currentParent.tNode.type === TNodeType.View ?
|
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;
|
currentParent as LElementNode;
|
||||||
|
|
||||||
for (let i = 0; i < nodesForSelector.length; i++) {
|
for (let i = 0; i < nodesForSelector.length; i++) {
|
||||||
|
@ -706,6 +706,171 @@ describe('content projection', () => {
|
|||||||
expect(toHtml(parent)).toEqual('<child><div></div></child>');
|
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', () => {
|
it('should project nodes into the last ng-content', () => {
|
||||||
/**
|
/**
|
||||||
* <div><ng-content></ng-content></div>
|
* <div><ng-content></ng-content></div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user