0

I'm setting up ngTemplateOutlet inside *ngFor like in below code snippet

  <ul>
    <li *ngFor="let item of list">
      <ng-container [ngTemplateOutlet]="item.type"></ng-container>
    </li>
  </ul>

Where list = [ {type: 'templateOne'}, {type: 'templateTwo'} ] and i have defined templates like below.

<ng-template #templateOne></ng-template>
<ng-template #templateTwo></ng-template>

The above template snippet is throwing error with message undermentioned

TypeError: templateRef.createEmbeddedView is not a function
    at ViewContainerRef_.push../node_modules/@angular/core/fesm5/core.js.ViewContainerRef_.createEmbeddedView (core.js:21600)
    at NgTemplateOutlet.push../node_modules/@angular/common/fesm5/common.js.NgTemplateOutlet.ngOnChanges (common.js:4026)
    at checkAndUpdateDirectiveInline (core.js:22085)

Since item.type used in ngTemplateOutlet is of type string i am suspecting it is not getting resolved to templateReference variable.

How can i convert string to a templateReference instance?

Demonstration - See this link for example and verify console for the error

  • Why you want convert string to template reference? Do you want to just show that string value? – Buczkowski May 06 '19 at 09:08
  • What data do the items keep inside the list? – talhature May 06 '19 at 09:14
  • I have defined [ngTemplateOutlet]="item.type" and expecting `item.type` to have different names corresponding to template reference variables. – Rakesh Maddala May 06 '19 at 09:16
  • 1
    The most appropriate answer i received is in github link below. [https://github.com/angular/angular/issues/30294#issuecomment-490393919](https://github.com/angular/angular/issues/30294#issuecomment-490393919) – Rakesh Maddala May 17 '19 at 07:13

3 Answers3

1

Here is working example:

    import { Component,ViewChild,TemplateRef,OnInit } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
  <ul>
     <li *ngFor="let item of list;let i = index">
      <ng-container [ngTemplateOutlet]="list[i].type"></ng-container>
    </li>
  </ul>
  <ng-template #templateOne>Template One</ng-template>
  <ng-template #templateTwo>Template Two</ng-template>
  `,
})
export class AppComponent  implements OnInit {
  @ViewChild('templateTwo', {read: TemplateRef}) tpl1: TemplateRef<any>;
  @ViewChild('templateOne', {read: TemplateRef}) tpl2: TemplateRef<any>;
  list;

    ngOnInit() {
      this.list=[{"type":this.tpl1},{"type":this.tpl2}];
    }

}

Reference link

Working example stackblitz

Niraj
  • 775
  • 7
  • 20
  • We will see the same error, see this example: https://stackblitz.com/edit/ng-template-outlet-kps7sl – Rakesh Maddala May 06 '19 at 10:17
  • This requires us to go over the response and update it with TemplateRef before we can use it in template which is overhead.. – Rakesh Maddala May 06 '19 at 14:36
  • That is not possible due to type safe binding you can do [ngIf] on template and use loop by ngFor on list and match and load only ng-template not required ng-container – Niraj May 07 '19 at 07:00
1

Your question is actually how do we treat a string as a templateReferance variable in a template html. If you do not have to handle it that way, you can just try this:

 @ViewChild('templateOne') templateOne: ElementRef;
 @ViewChild('templateTwo') templateTwo: ElementRef;
  list;
  ngOnInit() {
    this.list = [ {type: this.templateOne}, {type: this.templateTwo} ];
  }
talhature
  • 2,246
  • 1
  • 14
  • 28
1

Basically, you can create pure function that does mapping/conversion:

public map(type:string, ref1:TemplateRef, ref2:TemplateRef):TemplateRef {
   switch(type) {
      case 'templateTwo':
        return ref1;
      case 'templateTwo':
        return ref2;
      default:
        return ref1;
   }
}

Template:

<ul>
    <li *ngFor="let item of list">
      <ng-container [ngTemplateOutlet]="map(item.type, templateOne, templateTwo)"></ng-container>
    </li>
</ul>

Otherwise you will need access to template context:

public map(type:string):TemplateRef {
   return (this as any)[type]; // may break in future
}
kemsky
  • 14,727
  • 3
  • 32
  • 51