Home > Software engineering >  How can I give a component a template dynamically?
How can I give a component a template dynamically?

Time:03-22

I am trying to make it so that a parent component can pass an optional template to a child component, and the child component will use that as its template or fall back to a default template... Something like this:

# my.component.ts

import defaultTemplate from './template.html';

@Component({
  selector: 'my-component',
  template: '{{ dynamicTemplate }}'
)}
export class MyComponent implements OnInit {
   @Input() template: string;
   dynamicTemplate: string;

  ngOnInit(): void {
    this.dynamicTemplate = this.template || defaultTemplate;
  }
}
# parent.component.ts

import templateA from './template-a.html';
import templateB from './template-b.html';

@Component({
  selector: 'parent-component',
  template: `
    <p>some fun dynamic templates...</p>
    <my-component [template]="templateA"></my-component>
    <my-component [template]="templateB"></my-component>
    <my-component></my-component>
  `
)}
export class ParentComponent {
  templateA: string;
  templateB: string;
  constructor() {
    this.templateA = templateA;
    this.templateB = templateB;
  }
}

Except this results in escaped html.. What is the proper way to do this?

CodePudding user response:

Old answer that is not OP is asking

Not sure if I understood correctly, but would something like this work for you?

https://stackblitz.com/edit/angular-ivy-ngvgnd

basically

<my-component>
  <child-a></child-a>
</my-component>


<my-component>
  <child-b></child-b>
</my-component>


<my-component></my-component>

Edited the code to handle the html as an input.

import { Input, Component, OnInit } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';

@Component({
  selector: 'actual-answer',
  template: `
  <h1>parent html code</h1>
  <div [innerHtml]="htmlString"></div>
  `,
})
export class ActualAnsweComponent implements OnInit {
  @Input() yourHtml;
  htmlString;

  ngOnInit() {
    this.htmlString = this.sanitizer.bypassSecurityTrustHtml(this.yourHtml);
  }

  constructor(private sanitizer: DomSanitizer) {}
}

CodePudding user response:

See this answer: https://stackoverflow.com/a/44277384/12914833.

Short answer is you can't. Angular compiles the components into scripts, so changing the template at run time isn't possible. If you're just trying to inject some html into a template at runtime, that's doable. I'd suggest something like ngx-markdown for that (see end of answer).


Having a template be generated from an input string is way too unpredictable anyway. You should define all possible states of the template. You can make it dynamic with input variables and directives like ngIf.

For example, you could make a few components ComponentOne, ComponentTwo, ComponentThree to represent three different templates. And then the dynamic component could look like:

<app-component-one *ngIf="useComponentOne"></app-component-one>
<app-component-two *ngIf="useComponentTwo"></app-component-two>
<app-component-three *ngIf="useComponentThree"></app-component-three>
export class DynamicComponent {
  @Input() useComponentOne = false;
  @Input() useComponentTwo = false;
  @Input() useComponentThree = false;
}

And to use it

<app-dynamic-component [useComponentTwo]="true"></app-dynamic-component>

That makes the design much more predictable and testable.


If you just want to inject some html into a template, and you don't need it compiled with the component (ie. you're not trying to use angular directives or other components), ngx-markdown makes it easy.

<markdown>{{ htmlString }}</markdown>
export class MarkdownComponent {
  @Input() htmlString = "<h1>I'm an html header</h1>";
}
  • Related