1

Create Component template and Typescript code dynamically.

I am trying to generate the component on the fly, HTTP service call send us HTML template and Typescript code. In this we are able to set HTMl template dynamically but not typescript code, not able to figure out how to set typescript code dynamically.

* Please find below code which renders HTML template based on HTTP call. *

import { HttpClient, HttpClientModule } from "@angular/common/http";
import { Component, ViewChild, ViewContainerRef, Compiler, Injector, NgModule, NgModuleRef, ComponentFactoryResolver, OnInit } from "@angular/core";

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

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

  constructor(private http: HttpClient, private _compiler: Compiler, private componentFactoryResolver: ComponentFactoryResolver,
    private _injector: Injector,
    private _m: NgModuleRef<any>) {

  }
  ngOnInit() {
    return this.http.get("https://raw.githubusercontent.com/ketan-gote/stackblitz-demo/master/template.txt", { responseType: 'text' }).subscribe((txt: any) => {
      console.log(txt);
      const tmpCmp = Component({ template: txt, selector: 'abcd' })(class {

      });
      const tmpModule = NgModule({ imports: [HttpClientModule], declarations: [tmpCmp], entryComponents: [tmpCmp] })(class {
      });

      this._compiler.compileModuleAsync(tmpModule)
        .then((moduleFactory) => {
          debugger;
          const resolver = moduleFactory.create(this._injector).componentFactoryResolver;
          const f = resolver.resolveComponentFactory(tmpCmp);
          const cmpRef = f.create(this._injector, [], null, this._m);
          this._container.insert(cmpRef.hostView);
        })
    });

  }
}

Here's a working link !

* PROBLEM Now when we are trying to get TS code also as part of response it gives error core.js:12501 ERROR TypeError: Cannot read property '__annotations__' of undefined *

Please find below code which renders TS


import { HttpClient, HttpClientModule } from "@angular/common/http";
import { Component, ViewChild, ViewContainerRef, Compiler, Injector, NgModule, NgModuleRef, ComponentFactoryResolver, OnInit } from "@angular/core";

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

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

  constructor(private http: HttpClient, private _compiler: Compiler, private componentFactoryResolver: ComponentFactoryResolver,
    private _injector: Injector,
    private _m: NgModuleRef<any>) {

  }
  ngOnInit() {
    return this.http.get("https://raw.githubusercontent.com/ketan-gote/stackblitz-demo/master/ts.txt", { responseType: 'text' }).subscribe((txt: any) => {
      console.log(txt);
      const tmpCmp = Component({ template: "<h1>HEAD</h1>", selector: 'abcd' })(txt);
      const tmpModule = NgModule({ imports: [HttpClientModule], declarations: [tmpCmp], entryComponents: [tmpCmp] })(class {
      });

      this._compiler.compileModuleAsync(tmpModule)
        .then((moduleFactory) => {
          debugger;
          const resolver = moduleFactory.create(this._injector).componentFactoryResolver;
          const f = resolver.resolveComponentFactory(tmpCmp);
          const cmpRef = f.create(this._injector, [], null, this._m);
          this._container.insert(cmpRef.hostView);
        })
    });

  }
}

Here's is not working code link!

ketan gote
  • 11
  • 2

1 Answers1

0

i know it's late, but this can be done with 'eval' function.

Change the line from this

const tmpCmp = Component({ template: "<h1>HEAD</h1>", selector: 'abcd' })(txt);

to this

const tmpCmp = Component({ template: "<h1>HEAD</h1>", selector: 'abcd' })(eval('(' +txt+ ')'));

I found the answer in this post ( https://stackoverflow.com/a/14377840 ).