26

I am trying to build a listing component in Angular2 that takes the items, the columns and the templates for the fields of the items from the user of the component. So I am trying to use ngTemplateOutlet and ngOutletContext (which I have read are experimental). But I cannot get it to work.

Here is a simplified component to demonstrate what I am trying to do:

<div *ngFor="let item of items>
  <span *ngFor="let column of columns>
    <template [ngOutletContext]="{ item: item }" 
              [ngTemplateOutlet]="column.templateRef"></template>
  </span>
</div>

Here is the usage of the component:

<my-component [items]="cars" [columns]="carColumns">
  <template #model>{{item.model}}</template>
  <template #color>{{item.color}}</template>
  <template #gearbox>{{item.gearbox}}</template>
</my-component>

And here is example data:

cars = [
  { model: "volvo", color: "blue", gearbox: "manual" },
  { model: "volvo", color: "yellow", gearbox: "manual" },
  { model: "ford", color: "blue", gearbox: "automatic" },
  { model: "mercedes", color: "silver", gearbox: "automatic" }
];

carColumns = [
  { templateRef: "model" },
  { templateRef: "color" },
  { templateRef: "gearbox" }
];

Here is a plunker reproducing the issue after adapting the code according to Günters comment: https://plnkr.co/edit/jB6ueHyEKOjpFZjxpWEv?p=preview

luiscla27
  • 4,956
  • 37
  • 49
Hampus
  • 2,769
  • 1
  • 22
  • 38
  • 1
    Are you trying to pass `TemplateRef` as string? One way to do it https://plnkr.co/edit/3yM25cUVeP4fK0cxhpXp?p=preview – yurzui Nov 04 '16 at 09:12
  • One other way is using `ContentChildren` https://plnkr.co/edit/2Ohj12Gax6UaK6LJ5dwD?p=preview – yurzui Nov 04 '16 at 09:18
  • @yurzui Interesting. How does your version work and not mine? No, columns needs to be a list of objects as I pass additional information with it like header and if it currently should be showing or not. It seems the only difference between your plunker and mine is that you pass columns as a list of strings. – Hampus Nov 04 '16 at 09:25
  • `ngTemplateOutlet` param should be `TemplateRef` instance but in your case it's `string` – yurzui Nov 04 '16 at 09:29
  • @yurzui I do not understand what you are trying to say. In your (working) plunker it is also a string. Seems to be no issue. Where would I create a templateRef instance? – Hampus Nov 04 '16 at 09:32
  • 1
    In your template ` – yurzui Nov 04 '16 at 09:39

3 Answers3

28

This is how you need to do this:

@Component({
  selector: 'my-component',
  template: `
    <div *ngFor="let item of items">
      <span *ngFor="let column of columns">
        <template [ngTemplateOutlet]="column.ref" [ngOutletContext]="{ item: item }"></template>
      </span>
    </div>`
})
export class MyComponent {
  @Input() items: any[];
  @Input() columns: any[];
}

@Component({
  selector: 'my-app',
  template: `
    <div>
      <my-component [items]="cars" [columns]="columns">
        <template #model let-item="item">{{item?.model}}</template>
        <template #color let-item="item">{{item?.color}}</template>
      </my-component>
    </div>`
})
export class App {
  @ViewChild('model') model;
  @ViewChild('color') color;
  cars = [
      { model: "volvo", color: "blue" },
      { model: "saab", color: "yellow" },
      { model: "ford", color: "green" },
      { model: "vw", color: "orange" }
    ];

  ngAfterContentInit() {
    this.columns = [
      { ref: this.model },
      { ref: this.color ]
    ];
  }
}

Plunker

luiscla27
  • 4,956
  • 37
  • 49
Bazinga
  • 10,716
  • 6
  • 38
  • 63
15

update Angular 5

ngOutletContext was renamed to ngTemplateOutletContext

See also https://github.com/angular/angular/blob/master/CHANGELOG.md#500-beta5-2017-08-29

original

Don't use [] and {{}} together. Either the one or the other but not both.

Don't use {{}} if you want to pass an object because {{}} is for string interpolation.

It should be

[ngTemplateOutlet]="column.templateRef"

Plunker example

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
0

you can do it without ngOutletContext. You have to use ng-template instead of template. following code works for me:

app.component.html:

<div>
  <ng-template #c1>
    <app-child1></app-child1>
  </ng-template>
  <app-parent [templateChild1]="c1"></app-parent>
</div>

app-parent is a child of app-component. app-child is declared in app-component as template and this template is used in app-parent.

app.parent.html:

<p>
  <ng-template [ngTemplateOutlet]="templateChild1"></ng-template>
</p>

app.parent.ts:

@Component({
  selector: 'app-parent',
  templateUrl: './parent.component.html',
  styleUrls: ['./parent.component.css']
})
export class ParentComponent implements OnInit {

  @Input() public templateChild1: TemplateRef<any>;

  public ngOnInit() {
  }

}
troYman
  • 1,648
  • 16
  • 18