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

1

3 Answers 3

6

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);
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Sadly the author deleted the story :( how is/was the NgModule used?
Nevermind, here's an updated link: denys.dev/2016-11-27/dynamic-content-in-angular
1

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);

   });
 }
}

2 Comments

Yes, I have updated the answer to work with DomSanitizer
Tanks for your answers :). But innerhtml is for static html ? it's still working if poData = "<mycustomcomponent></mycustomcomponent>" ?
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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.