How do I get experimental ngTemplateOutlet to work?
Asked Answered
D

3

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

Dialectologist answered 4/11, 2016 at 8:56 Comment(6)
Are you trying to pass TemplateRef as string? One way to do it plnkr.co/edit/3yM25cUVeP4fK0cxhpXp?p=previewIntuitive
One other way is using ContentChildren plnkr.co/edit/2Ohj12Gax6UaK6LJ5dwD?p=previewIntuitive
@Intuitive 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.Dialectologist
ngTemplateOutlet param should be TemplateRef instance but in your case it's stringIntuitive
@Intuitive 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?Dialectologist
In your template <template #model> model will be TemplateRef instance. If you want to use model as TemplateRef in your component then you should look at ViewChild(ViewChildren) #39909467 See also angular.io/docs/ts/latest/guide/template-syntax.html#!#ref-varsIntuitive
O
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

Obscurant answered 4/11, 2016 at 9:34 Comment(4)
Thanks a bunch. I wasn't far off but far enough to feel the urge to bang my head against the wall.Dialectologist
I am surprised you need to explicitly say let-item="item". Adding a let solved a similar problem I was having. You'd think this would be implied.Scant
{ ref: this.color ] here is not a brackets, and after 4.0 change <ng-template>Carbon
Plunkr updated for 5 plnkr.co/edit/HuQOYuNfyzAvQrXRlBLf?p=previewLutetium
P
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

Pelasgian answered 4/11, 2016 at 8:58 Comment(6)
Ahh, makes sense. Still not working though. Now I get core.umd.js:3072 ORIGINAL EXCEPTION: templateRef.createEmbeddedView is not a functionDialectologist
Please provide a Plunker to reproduce. It's difficult to debug with just some code fragments.Maltz
I added a plunker.Dialectologist
Came to the same conclusion as PatrickJane but he was faster with the Plunker.Maltz
Thanks anyway for helping out. It is not a competition.Dialectologist
I just didn't want it to look like I copied the plunker.Maltz
E
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() {
  }

}
Episcopacy answered 4/10, 2019 at 10:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.