angularangular-template

Render an Angular string template at runtime


I have a component that makes a request to fetch an Angular template from the server and render it at runtime. Please keep in mind that the template is an Angular formatted template using Angular pipes and directives, so it's not something like using lodash templates. A very simple example of this template would be:

<ul>
  <li *ngFor="let item of items">
    {{ item.name | titlecase }}
  </li>
</ul>

In my component I've tried doing the following that excludes actually fetching the template from the server for my example for simplicity sake:

@Component({
  selector: 'app-landing-page',
  template: `<div [innerHtml]="template"></div>`,
})
export class LandingPageComponent {
  @ViewChild('dynamicComponentContainer', { read: ViewContainerRef, static: true })
  container: ViewContainerRef;

  ngAfterViewInit() {
    const componentRef = this.container.createComponent(DynamicTemplateComponent);
    componentRef.instance.items = [{ name: 'foo' }, { name: 'bar' }, { name: 'baz' }];
    componentRef.instance.template = `<ul>
      <li *ngFor="let item of items">
        {{ item.name | titlecase }}
      </li>
    </ul>`;
  }
}

And here is the DynamicTemplateComponent that is used to create the component to dynamically render the Angular template:

@Component({
  selector: 'app-dynamic-template',
  template: `<div [innerHtml]="template"></div>`,
})
export class DynamicTemplateComponent {
  @Input() template: string;
  @Input() items: any[];
}

Unfortunately this doesn't produce any results. Any help is much appreciated.


Solution

  • You can create dynamic components with a custom template and any other properties.

    In this case if you want to dynamically change decorated properties on a component creation then it needs to be manually compiled async. Please check the example below:

      ngOnInit(): void {
        const template = `<div *ngFor="let item of items">{{ item }}</div>`;
        const component = getComponentFromTemplate(template);
    
        const componentRef = this.viewRef.createComponent(component);
        componentRef.setInput('items', ['value1', 'value2']);
      }
    
    @Component({
        template: '',
      }) class MyCustomComponent {
        @Input() items: string[] = [];
    }
    
    function getComponentFromTemplate(template: string) {
      ɵcompileComponent(MyCustomComponent, {
        template,
        standalone: true,
        imports: [NgFor],
      });
    
      return MyCustomComponent;
    }
    

    And there is example on stackblitz: working example