0

Is there anyway load component to HTMLElement throught code? I tried this:

        var note = $(`
        <app-test></app-test>`);
        $(container).append(note)

but it not working, but when i try

        var note = $(`
        <h1>Hi</h1>`);
        $(container).append(note)

it works perfect. whats the problem?

Paxton.Huynh
  • 172
  • 10

1 Answers1

2

Let's try this Firstly, we create a DomService to help you attach/detach component from container

import {
  ApplicationRef,
  ComponentFactoryResolver,
  ComponentRef,
  EmbeddedViewRef, Inject,
  Injectable,
  InjectionToken,
  Injector
} from '@angular/core';
import {ComponentPortal} from '@angular/cdk/portal';
import {DOCUMENT} from '@angular/common';

export const MY_TOKEN = new InjectionToken<{}>('MY_TOKEN');

@Injectable({
  providedIn: 'root'
})
export class DomService {

  constructor(
    @Inject(DOCUMENT) private document: Document,
    private componentFactoryResolver: ComponentFactoryResolver,
    private appRef: ApplicationRef,
  ) {
  }

  private _createInjector(useValue): Injector {
    return Injector.create({
      providers: [{provide: MY_TOKEN, useValue}]
    });
  }

  createComponent(
    component: any,
    appendTo: Element,
    componentProps: {},
    wrapperClass: string
  ): ComponentRef<unknown> {
    const componentRef = this.componentFactoryResolver
      .resolveComponentFactory(component)
      .create(this._createInjector(componentProps));

    this.appRef.attachView(componentRef.hostView);

    const domElem = (componentRef.hostView as EmbeddedViewRef<any>)
      .rootNodes[0] as HTMLElement;

    if (wrapperClass) {
      const hasWrapper = appendTo.querySelector(`.${wrapperClass}`);
      if (!hasWrapper) {
        const divTag = this.document.createElement('div');
        divTag.className = wrapperClass;
        appendTo.appendChild(divTag);
      }

      appendTo = appendTo.querySelector(`.${wrapperClass}`);
    }

    appendTo.appendChild(domElem);

    return componentRef;
  }

  detachComponent(componentRef: ComponentRef<unknown>): void {
    this.appRef.detachView(componentRef.hostView);
    componentRef.destroy();
  }
}

Secondly, in your component, you just inject the above service and use it.

For example, you want to append AppTestComponent into <div id="container"></div>

export class AppTestComponent {
    name: string;
    constructor(
    @Inject(MY_TOKEN) public token: any
    ) {
      this.name = token.name;
    }
}
export class MyComponent implements OnInit, OnDestroy {
   componentRef: ComponentRef<any>;
   constructor(
   @Inject(DOCUMENT) private document: Document,
   private domService: DomService,
   ){}
  
  ngOnInit() {
     this.componentRef = this.domService
         .createComponent(
              AppTestComponent,
              this.document.getElementById('container'),
              {name: 'just for test'},
              'app-test-container'
            );
  }

 ngOnDestroy(): void {
    this.domService.detachComponent(this.componentRef);
 }
}
ttquang1063750
  • 803
  • 6
  • 15
  • This technique was ideal for binding the Golden Layout library into Angular: [https://github.com/golden-layout/golden-layout-ng-app](https://github.com/golden-layout/golden-layout-ng-app). Thanks. – Paul Klink Feb 10 '21 at 00:59
  • @PaulKlink, I tried to modify your demo project to my application but in prod mode, Unknown component type error thrown. What should I do ? – whitefang Apr 10 '21 at 18:59
  • I defined static key to every component and changed name with key as defined this question -> https://stackoverflow.com/questions/40528592/ng2-dynamically-creating-a-component-based-on-a-template/40662376#40662376. thanks for your github golden-layout example @PaulKlink. – whitefang Apr 10 '21 at 19:59