Angular 2 Template as a parameter to component
Asked Answered
B

4

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

Blip answered 29/4, 2016 at 21:42 Comment(0)
L
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

Lunneta answered 30/4, 2016 at 22:5 Comment(1)
you anyway need provide a template for *ngForTemplateLunneta
A
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>
Aelber answered 5/4, 2017 at 18:46 Comment(4)
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?Voice
@giannischristofakis You can use any component and bind event handlers between the template: <template ...><my-component event="onEvent()" /></template>Aelber
Thanks @Aelber you saved my day. I had to use <ng-template [ngTemplateOutlet]="template" [ngTemplateOutletContext]="{item: item}"></ng-template> though in order to work.Voice
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...Seychelles
L
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

Lunneta answered 30/4, 2016 at 22:5 Comment(1)
you anyway need provide a template for *ngForTemplateLunneta
A
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(){

  }
}
Ariellearies answered 30/4, 2016 at 4:4 Comment(4)
I need to repeat template with local variables passed, so each item is different.Blip
if you are using ngFor , how can it be different ?Ariellearies
to have differnt content you need to put it repeatedly in template like this <list> item </list> <list> item2 </list> <list> item3 </list>Ariellearies
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.Blip
P
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

Plagiarism answered 30/4, 2016 at 14:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.