Search code examples
angulartypescriptangular-directiveangular5

Dynamically append component to div in Angular 5


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


Solution

  • Here's the way I got it working

    import {
      Renderer2,
      Directive,
      Input,
      ElementRef,
      OnChanges,
      ViewEncapsulation
    } from "@angular/core";
    import { MatSpinner } from "@angular/material";
    
    @Directive({
      selector: "[myDirective]"
    })
    export class MyDirective {
    
      @Input()
      set myDirective(newValue: boolean) {
        console.info("myDirectiveBind", newValue);
        if (!!this._$matCard) {
          const method = newValue ? "removeClass" : "addClass";
          this.renderer[method](this._$matCard, "ng-hide");
        }
        this._myDirective = newValue;
      }
    
      private _myDirective: boolean;
      private _$matCard;
    
      constructor(private targetEl: ElementRef, private renderer: Renderer2) {
        this._$matCard = this.renderer.createElement('mat-card');
        const matCardInner = this.renderer.createText('Dynamic card!');
        this.renderer.addClass(this._$matCard, "mat-card");
        this.renderer.appendChild(this._$matCard, matCardInner);
        const container = this.targetEl.nativeElement;
        this.renderer.appendChild(container, this._$matCard);
      }
    
    
    }
    
    import {
      Component,
      ElementRef,
      AfterViewInit,
      ViewEncapsulation
    } from '@angular/core';
    
    @Component({
      selector: 'card-overview-example',
      templateUrl: 'card-overview-example.html',
      styleUrls: ['card-overview-example.css']
    })
    export class CardOverviewExample {
      
      hideMyDirective = !1;
    
      constructor(private _elementRef: ElementRef) { }
    
      getElementRef() {
        return this._elementRef;
      }
    
      ngAfterViewInit() {
        let element = this._elementRef.nativeElement;
        let parent = element.parentNode;
        element.parentNode.className += " pippo";
    
      }
    }
    .ng-hide {
      display: none;
    }
    <mat-card>Simple card</mat-card>
    <div class="text-center">
      <button (click)="hideMyDirective = !hideMyDirective">
        Toggle show dynamic card
    </button>
    </div>
    <br />
    <span>hideMyDirective: {{hideMyDirective}}</span>
    <hr />
    <div class="myDiv" [myDirective]="hideMyDirective">
        <ul>
          <li>My content</li>
          </ul>
    </div>