5

I have a simple question: in a simple Angular component, could we change dynamically the template retrieved by a http call for example:

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';


/**
 * Les liens link permettent de passer d'une page à l'autre.
 */
@Component({
  selector: 'mycomponent',
  template: '<strong>Loading…</strong>'
})
export class MyComponent implements OnInit {

  //#region PROPRIÉTÉS
    private moHttp : HttpClient
  //#endregion

  //#region CONSTRUCTEUR
  constructor(poHttp: HttpClient){
    this.moHttp = poHttp;
  }

  public ngOnInit(): void {
    this.moHttp.get('https://myapiUrl').subscribe(poData:any => {

      // here poData is HTML string, and I want to set it instead of the "<strong>Loading…</strong>"

    });
  }

  }
  //#endregion

Thank you in advance

  • Possible duplicate of [Angular 2: How can I apply directives to sanitized html/innerhtml](https://stackoverflow.com/questions/46217049/angular-2-how-can-i-apply-directives-to-sanitized-html-innerhtml) – Baruch Aug 17 '18 at 08:42

3 Answers3

5

Angular does not support dynamic templates natively. You can either use lazy loading or update views directly through the DOM.

... or there is a very hacky hack to implement that, thanks to DenisVuyka: Full Article

Here we need to create NgModule to create component factory and use Component decorator to pass metadata such as template and providers to component class.

@Component({
    selector: 'runtime-content',
    template: `<div #container></div>`
})    
export class RuntimeContentComponent {
    constructor(public componentRef: ComponentRef, private compiler: Compiler){}

    @ViewChild('container', { read: ViewContainerRef })
    container: ViewContainerRef;

    public compileTemplate(template) {
        let metadata = {
           selector: `runtime-component-sample`,
           template: template
        };

        let factory = this.createComponentFactorySync(this.compiler, metadata, null);

        if (this.componentRef) {
            this.componentRef.destroy();
            this.componentRef = null;
        }
        this.componentRef = this.container.createComponent(factory);
    }

    private createComponentFactorySync(compiler: Compiler, metadata: Component, componentClass: any): ComponentFactory<any> {
        const cmpClass = componentClass || class RuntimeComponent { name: string = 'Denys' };
        const decoratedCmp = Component(metadata)(cmpClass);

        @NgModule({ imports: [CommonModule], declarations: [decoratedCmp] })
        class RuntimeComponentModule { }

        let module: ModuleWithComponentFactories<any> = compiler.compileModuleAndAllComponentsSync(RuntimeComponentModule);
        return module.componentFactories.find(f => f.componentType === decoratedCmp);
    }
}
0

You can do the following assuming your poData is a string,

@Component({
  selector: 'mycomponent',
  template: '<div [innerHTML]="myContent"></div>'
})
export class MyComponent implements OnInit {
   private moHttp : HttpClient;
   myContent: any= '<strong>Loading…</strong>';

   constructor(poHttp: HttpClient, private sanitizer: DomSanitizer){
      this.moHttp = poHttp;
   }

   public ngOnInit(): void {
      this.moHttp.get('https://myapiUrl').subscribe(poData:any => {

      this.myContent = this.sanitizer..bypassSecurityTrustHtml(poData);

   });
 }
}
Senal
  • 1,510
  • 1
  • 14
  • 22
0

Try using the DomSanitizer

Note: You don't have to create a new field to keep the injected services.
constructor(private http: HttpClient){} will allow you to use the httpClient (as this.http) anywhere in the class.

Paulie
  • 164
  • 5