Dynamically append component to div in Angular 5

Sampgun picture Sampgun · Jan 23, 2018 · Viewed 27.9k times · Source

I have this

https://angular-dynamic-component-append.stackblitz.io/

I managed to dynamically append an element, but it doesn't get compiled. I saw many tutorials like this

But it's not really what I need. And often they use the hashtag notation to identify the container.

I need to append a component to any element which may have my custom directive on it.

I'd also need to use the bind value of the directive to control a [hidden] attribute on the appended element.

THE GOALS

  1. Override behaviour of existing component:
    • adding an attribute to show/hide
    • adding a class to customize appearance
  2. Reduce html coding
    • No need to write the entire component <my-comp></mycomp>
    • No need to know the class
    • Automatic behaviour if the class name is changed
      1. Changing the element on which the directive is applied
    • The final goal will be to add a class to the contaner element

Expected source

<div [myDirective]="myBoolean">
    <p>some content</p>
</div>

Expected compiled

<div [myDirective]="myBoolean" class="myDirectiveClass1">
    <p>some content</p>
     <someComponent [hidden]="myBoolean" class="myDirectiveClass2"></someComponent>
</div>

Is there a way to achieve this?

Thank you in advance

Answer

Leandro Lima picture Leandro Lima · Jan 24, 2018

It's pretty simple. I just made an example to you.

Please, read the comments inside loader directive.

https://github.com/garapa/studying/tree/master/loader

EDIT:

You component:

export class LoaderComponent {

  loading;

  constructor() { }

}

Your directive

export class LoaderDirective implements OnDestroy {

  private componentInstance: ComponentRef<LoaderComponent> = null;

  @Input()
  set appLoader(loading: boolean) {
    this.toggleLoader(loading);
  }

  constructor(
    private viewContainerRef: ViewContainerRef,
    private componentFactoryResolver: ComponentFactoryResolver
  ) { }

  toggleLoader(loading: boolean) {
    if (!this.componentInstance) {
      this.createLoaderComponent();
      this.makeComponentAChild();
    }

    this.componentInstance.instance.loading = loading;
  }

  private createLoaderComponent() {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(LoaderComponent);
    this.componentInstance = this.viewContainerRef.createComponent(componentFactory);
  }

  private makeComponentAChild(){
    const loaderComponentElement = this.componentInstance.location.nativeElement;
    const sibling: HTMLElement = loaderComponentElement.previousSibling;
    sibling.insertBefore(loaderComponentElement, sibling.firstChild);
  }

  ngOnDestroy(): void {
    if (this.componentInstance) {
      this.componentInstance.destroy();
    }
  }

}

You module

@NgModule({
  ...
  entryComponents: [
    LoaderComponent
  ]
})