Search code examples
angularangular2-templateangular-components

Use ng-template in the child component from the parent component


I have the following component for render a list of options. optionsList input gets a list that needs to be rendered with a custom format provided from the item input.

list-box.component.html

<div *ngFor="let option of optionsList; index as it">
  <ng-container [ngTemplateOutlet]="item"></ng-container>
</div>

list-box.component.ts

@Component({
  selector: 'app-list-box',
  templateUrl: './list-box.component.html'
})
export class ListBoxComponent implements OnInit {
  @Input('item') public item!:TemplateRef<any>;
  @Input('optionsList') optionsList:any[]=[];
  ...
}

Then, in a parent component I have a template that contains the format for render each item, and send it to the list-box component for the task:

In the parent.component.html

<ng-template let-option pTemplate="item" #item>
  <label>{{it}}: {{option.name}}</label>
</ng-template>

<app-list-box
  [optionsList]="[{'name': '...'}, ...]"
  [item]="item">
</app-list-box>

This code obviously doesn't work, it shows the error that option and it don't exist in the parent.

The question is, How can I send a particular format to render each item in the child component?.

I can use a specific format in the child component, but if the list of items has other type of items with different fields, then the list-box component is not useful, and I would need to create another specific component or map the content from the list to set the names of the required fields, but it is not desired.

P.S. For this writing, I simplified the code using ngfor, because in practical terms it is what I require. I don't want to put the ngFor in the parent to set all the items there, the sample code is a simulation of an imported module that I want to customize.


Solution

  • Taking into account the following code:

    parent.component.html:

    <ng-template let-option pTemplate="item" #item>
      <label>{{it}}: {{option.name}}</label>
    </ng-template>
    
    <app-list-box
      [optionsList]="[{'name': '...'}, ...]"
      [item]="item">
    </app-list-box>
    
    1. ✓ I can see that the parameter let-option is implicit, this is because it is not being assigned to a specific element of the context. (example: let example="something") [Valid]

    2. 🗙 The variable "it" has not been defined, which can generate an error. [Invalid]


    Taking into account that {{ it }} means the current position of the loop, can be done as follows:

    parent.component.html:

    <ng-template let-option let-it="index" #item>
      <label>{{it}}: {{option.name}}</label>
    </ng-template>
    
    <app-list-box
      [optionsList]="[{'name': '...'}, ...]"
      [item]="item">
    </app-list-box>
    

    list-box.component.html:

    <div *ngFor="let option of optionsList; let i = index">
      <ng-container *ngTemplateOutlet="item; context: {$implicit: option, index: i}"></ng-container>
    </div>
    

    It is important to note that:

    $implicit -> will be assigned to let-option
    index -> will be assigned to let-it

    Test demo: https://codesandbox.io/s/elegant-sea-diiu5x?