Angular 6 ngTemplateOutlet inside ngFor multiple context

Karthik Prabuddha picture Karthik Prabuddha · Jun 13, 2018 · Viewed 9.4k times · Source

let's assume I have the following component:

@Component({
  selector: 'todo-lib',
  template: `
<li *ngFor="let todo of libService.todos">
  <div class="view">
    <label>{{todo.title}}</label>
    <ng-container *ngTemplateOutlet="incomingTemplate;context:ctx"></ng-container>
  </div>
</li>

<ng-template #incomingTemplate let-service="templateService">
<button (click)="service.removeTodo(todo)">delete</button>
</ng-template>
`,
  styles: []
})
export class TodoLibComponent implements OnInit {

  ctx = {
    templateService: this.libService
  };

The list of todos is inside the libService. I don't publish it here, since its logic doesn't matter. The "ng-template" will be injected from another component and will add a delete button to my todo list. That means: Typically there is no delete option, except the "ng-template" will be provided

However, this approach will fail with the following error:

message: "_v.context.todo is undefined"

since "todo" is not part of the context object 'ctx' I can't access it. To achieve this I would be forced to do it like this:

<ng-container *ngTemplateOutlet="incomingTemplate;context:{ todo: todo}">

This would let me access the todo object but not the service anymore.

It seems that I can't access the 'todo' property defined by the ngFor AND the service defined in the 'ctx' property at the same time. Is there a solution to achieve both?

Cheers

Answer

Karthik Prabuddha picture Karthik Prabuddha · Jun 15, 2018

I also mentioned that on the official github repository of Angular. The correct answer by the devs was:

@dawidgarus: You can do:

<ng-container *ngTemplateOutlet="incomingTemplate;context:{ todo: todo, templateService: libService }">

In addition:

@dawidgarus suggestion is a good one and follows how scoping works in current Angular - I don't think we want to re-visit it. The only thing I would suggest is to use <ng-template> element instead of <ng-container> as it generates less code, doesn't create unnecessary comment nodes in the DOM etc.:

<ng-template [ngTemplateOutlet]="incomingTemplate" [ngTemplateOutletContext]="{ todo: todo, templateService: libService }">

so I used ng-template instead of ng-container.

Cheers