Sunny Sunny - 2 months ago 202
Javascript Question

Nested Templates in Angular 2

I have a component,

<DropDown></DropDown>
and I want to let the user pass in a template for the list items in the DropDown.

Assuming they want to make a custom list item that has an image and text they would do something like this:

<DropDown [data]="myData">
<template>
<span> <img src="..."> Some Text <span>
</template>
</DropDown>


Inside the HTML of my
DropDown
component I have:

<div>
<ul>
<DropDownList [data]="data">
</DropDownList>
</ul>
</div>


In the DropDownList component I have the following HTML:

<li *ngFor="let item of data
(click)="handleOnSelect(item)">
[class.selected]="selectedItems.includes(item)">

<template [ngWrapper]="itemWrapper>
</template>
</li>


(I am using the template wrapper method from this post:
Binding events when using a ngForTemplate in Angular 2)

This method works if I have the li elements within the DropDown component's HTML. However, I want to have wrap the li's into a DropDownList component and pass the template the user gave from DropDown into DropDownList.

Is it possible to do this?

Answer

You could try the following solution:

@Component({
  selector: 'DropDownList',
  template: `
   <li *ngFor="let item of items" (click)="handleOnSelect(item)">
    <template [ngTemplateOutlet]="itemWrapper" [ngOutletContext]="{ $implicit: item }">
    </template>
   </li>`
})
export class DropDownListComponent {
  @Input() itemWrapper: TemplateRef<any>;
  @Input() items: any;
  handleOnSelect(item) {
   console.log('clicked');
  }
}

@Component({
  selector: 'DropDown',
  template: `
    <div>
      <ul>
          <DropDownList [items]="items" [itemWrapper]="itemWrapper">
          </DropDownList>
      </ul>
    </div>`
})
export class DropDownComponent {
  @Input() items: string[];
  @ContentChild(TemplateRef) itemWrapper: TemplateRef<any>;
} 

@Component({
  selector: 'my-app',
  template: `
     <DropDown [items]="items">
       <template let-item>
            <h1>item: {{item}}</h1>
       </template>
    </DropDown>
  `
})
export class App { 
   items = ['this','is','a','test'];
}

Plunker Example

The ngTemplateOutlet(^2.0.0-rc.2) directive has the same functionality as your custom directive NgWrapper

See also related questions: