How can I transclude/project into a slot that is within a loop, and have the projected content able to access the loop variables?
Say I have a base ponent with the following
<tr *ngFor="let data of items">
<td>{{data.title}}</td>
<ng-content select="[slot]"></ng-content>
</tr>
And a child ponent that uses the transclusion slot "slot"
<parent [items]="items">
<ng-container slot>
<td>{{data.category}}</td>
<td>{{data.number}}</td>
</ng-container>
</parent>
The HTML I'd like generated is
<tr>
<td>{{data.title}}</td>
<td>{{data.category}}</td>
<td>{{data.number}}</td>
</tr>
But what actually happens is that "data" is not defined in the child ponent, which makes sense. Is there any way I can get it to work like this?
How can I transclude/project into a slot that is within a loop, and have the projected content able to access the loop variables?
Say I have a base ponent with the following
<tr *ngFor="let data of items">
<td>{{data.title}}</td>
<ng-content select="[slot]"></ng-content>
</tr>
And a child ponent that uses the transclusion slot "slot"
<parent [items]="items">
<ng-container slot>
<td>{{data.category}}</td>
<td>{{data.number}}</td>
</ng-container>
</parent>
The HTML I'd like generated is
<tr>
<td>{{data.title}}</td>
<td>{{data.category}}</td>
<td>{{data.number}}</td>
</tr>
But what actually happens is that "data" is not defined in the child ponent, which makes sense. Is there any way I can get it to work like this?
Share Improve this question edited Jan 9, 2018 at 23:08 dhilt 20.9k8 gold badges79 silver badges93 bronze badges asked Feb 13, 2017 at 22:22 riyuyuriyuyu 1672 silver badges8 bronze badges 7- Where are you calling the child ponent? – developer033 Commented Feb 13, 2017 at 22:24
-
@developer033 I'm calling the child ponent in another ponent. So
<child-ponent></child-ponent>
The child ponent grabs the data that goes into the item via xhr and passes it into the parent like this: <parent [items]="items"> <ng-container slot> <td>{{data.category}}</td> <td>{{data.number}}</td> </ng-container> </parent> – riyuyu Commented Feb 13, 2017 at 22:25 - Exactly right. I'm trying to get "data" - the loop variable into the child ponent like you would normally in twig for example – riyuyu Commented Feb 13, 2017 at 22:30
- it sounds like you want this: toddmotto./… – user9903 Commented Feb 13, 2017 at 22:36
- @RudolfOlah I've seen that tutorial. I'm using the multi-slot transclusion/projection, but the issue here is that I'm using a slot which is in a loop. – riyuyu Commented Feb 13, 2017 at 22:44
2 Answers
Reset to default 3Using TemplateRef
it is possible to declare template variables acting between two ponents declaratively, on templates level. The following solution does not fully match your "slot" infrastructure but could be useful to further investigation.
list.ponent.ts
import { Component, Input, ContentChild, TemplateRef } from '@angular/core';
@Component({
selector: 'list',
template: `
<tr class="box" *ngFor="let data of items">
<td>{{data.title}}</td>
<ng-template
[ngTemplateOutlet]="template"
[ngTemplateOutletContext]="{ $implicit: data }">
</ng-template>
</tr>`
})
export class ListComponent {
@Input() items;
@ContentChild(TemplateRef) template: TemplateRef;
constructor() { }
}
wrapper.ponent.ts
import { Component, ContentChild, TemplateRef } from '@angular/core';
@Component({
selector: 'wrapper',
template: `
<table>
<list [items]="items">
<ng-template let-data>
<td>{{data.category}}</td>
<td>{{data.number}}</td>
</ng-template>
</list>
</table>`
})
export class WrapperComponent {
items = [
{ title: 'T1', category: 'C1', number: 'N1' },
{ title: 'T2', category: 'C2', number: 'N2' },
{ title: 'T3', category: 'C3', number: 'N3' }
];
@ContentChild(TemplateRef) template: TemplateRef;
constructor() { }
}
I also created a Plunker demo...
@dhilt's answer is a good implementation, but I have a way to make it useful for named slots as well as single slots:
// list.ponent.ts
import { Component, Input, TemplateRef } from '@angular/core';
@Component({
selector: 'list',
template: `
<tr class="box" *ngFor="let data of items">
<td>{{data.title}}</td>
<ng-template
[ngTemplateOutlet]="template"
[ngTemplateOutletContext]="{ $implicit: data }">
</ng-template>
</tr>`
})
export class ListComponent<T extends {title: any}> {
@Input() items: T[];
@Input() template: TemplateRef<{$implicit: T}>;
constructor() { }
}
// wrapper.ponent.ts
import { Component } from '@angular/core';
@Component({
selector: 'wrapper',
template: `
<table>
<list [items]="items" [template]="itemTemplate"></list>
<ng-template #itemTemplate let-data>
<td>{{data.category}}</td>
<td>{{data.number}}</td>
</ng-template>
</table>`
})
export class WrapperComponent {
items = [
{ title: 'T1', category: 'C1', number: 'N1' },
{ title: 'T2', category: 'C2', number: 'N2' },
{ title: 'T3', category: 'C3', number: 'N3' }
];
constructor() { }
}
Here is the API docs for *ngTemplateOutlet
. Even other directives like *ngIf
use templates in that manner.