37

My simplified goal is to build a component which is a list with item template. E.g.:

<list>item</list>

Here is my code:

import {bootstrap} from 'angular2/platform/browser';
import {Component} from 'angular2/core';

@Component({
  selector: 'list',
  template: `
    <ul>
      <li *ngFor="let i of items" >
        <ng-content></ng-content>
      </li>
    </ul>
  `
})
class List {
  items = [1, 2, 3];
}

@Component({
  selector: 'app',
  directives: [List],
  template: '<list>item</list>'
})
class App { }

bootstrap(App, []);

Expected result:

  • item
  • item
  • item

Actual result:



• item

Cobus Kruger
  • 8,338
  • 3
  • 61
  • 106
Dizzy
  • 892
  • 3
  • 12
  • 24

4 Answers4

51

This is how I did it:

Usage:

<ng-template #myTemplate let-item="item">
    <strong>Name: </strong> {{item.name}}<br>
    <strong>Id: </strong> {{item.id}}
</ng-template>

<app-template-param [items]="items" [template]="myTemplate"></app-template-param>

Component:

@Component({
   selector: 'app-template-param',
   templateUrl: 'template-param.html'
})
export class TemplateParamComponent implements OnInit {
    @Input() items: Array<any>;
    @Input() template: TemplateRef<any>;
}

Component HTML

<ng-template #defaultTemplate let-item="item">
    <strong>{{item.name}}</strong>
</ng-template>
<ul>
    <li *ngFor="let item of items">
        <ng-template [ngTemplateOutlet]="template || defaultTemplate" [ngTemplateOutletContext]="{item: item}"></ng-template>
    </li>
</ul>
cloakedninjas
  • 4,007
  • 2
  • 31
  • 45
RVandersteen
  • 2,067
  • 2
  • 25
  • 46
  • 1
    Can you please add the Component annotation for the `TemplateParamComponent` so we can see the selector and the template URL? Moreover, what if the template is a separate component, with buttons that emit events? – giannis christofakis Nov 12 '18 at 11:45
  • 1
    @giannischristofakis You can use any component and bind event handlers between the template: – RVandersteen Nov 12 '18 at 14:09
  • 3
    Thanks @RVandersteen you saved my day. I had to use `` though in order to work. – giannis christofakis Nov 16 '18 at 08:06
  • I've tried to use this for my scenario, but my template HTML is always empty. My scenario is as follows: my component needs to have a templateref input which represents a popup markup shown in specific cases. The content of each popup is different, because it depends on another input parameter... – Alexander Aug 07 '22 at 14:01
15

I found 3 ways to do it

@Component({
   selector: 'dynamic-list',
   template: '<div *ngFor="#item of items" *ngForTemplate="itemTemplate"></div>'
})    
export class DynamicListComponent {

  @ContentChild(TemplateRef)
  public itemTemplate: TemplateRef;

  @Input()
  public items: number[];
}




<dynamic-list [items]="items">
  <div template="#item">
      Inline template item #: {{item}}
  </div>
</dynamic-list> 

output:

Inline template item #: 1
Inline template item #: 2
Inline template item #: 3
Inline template item #: 4

plunker: https://plnkr.co/edit/ollxzUhka77wIXrJGA9t?p=preview

see more https://github.com/ilio/ng2-dynamic-components/blob/master/README.md

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
IgorL
  • 1,169
  • 10
  • 19
2

PLUNKER

AFAI tried <ng-content> can't be binded repeatedly, but you can pass item as input, like this

import {Component} from 'angular2/core';

@Component({
  selector: 'list',
  inputs: ['htmlIN'],
  template: `
    <ul>
      <li *ngFor="#i of items" >
       {{i}} {{htmlIN}}
      </li>
    </ul>
  `
})
export class List {
  htmlIN: any;
  items = [1, 2, 3, 4];
}

@Component({
  selector: 'my-app',
  directives: [List],
  template: `<list htmlIN="item"></list>`
})
export class App { 
  constructor(){

  }
}
Ankit Singh
  • 24,525
  • 11
  • 66
  • 89
  • 1
    I need to repeat template with local variables passed, so each item is different. – Dizzy Apr 30 '16 at 07:56
  • if you are using ngFor , how can it be different ? – Ankit Singh Apr 30 '16 at 08:04
  • to have differnt content you need to put it repeatedly in template like this ` item item2 item3 ` – Ankit Singh Apr 30 '16 at 08:05
  • I think this should be handled via Dependency Injection so item template will resolve to what we need. However I'm not sure how to handle different templates simultaneously using DI. – Dizzy Apr 30 '16 at 09:26
0

I would consider changing slightly the design. Instead of inserting content between <list> and </list> and reference such content via ng-content in the List component, I would create a Component that plays the role of content (i.e. "repeat template with local variables passed, so each item is different") with the required input variables and insert the Component into the list. In other words something like this

ContentComponent

@Component({
  selector: 'my-content',
  inputs: ['inputVar'],
  template: `<div>Item no. {{inputVar}}</div>`
})
export class MyContent { 
  inputVar: string;
}

List

@Component({
  selector: 'list',
  directives: [MyContent],
  template: `
    <ul>
      <li *ngFor="let i of items" >
        <my-content [inputVar]="i"></my-content>
      </li>
    </ul>
  `
})
class List {
  items = [1, 2, 3];
}

I hope it helps

Picci
  • 16,775
  • 13
  • 70
  • 113