简体   繁体   中英

Angular Delete dynamic component

I am creating dynamic components to push toasters (notifiers or notification) with the help of Angular dynamic component using

- ComponentFactoryResolve
-  EmbeddedViewRef
-  ApplicationRef
-  ComponentRef
-  Injectable
-  Injector

what I have done so far is creating the component to be pushed and service for it and registered it in the module and it pushes the component fine but the problem is when I am trying to delete the component it gets a reference to the last pushed component.

toast.component.ts (the dynamic component)


@Component({
  selector: 'uni-toast',
  template: `
    <div>
      <p (click)="closeToast()">{{ type }}</p>
      <p>{{ msg }}</p>
    </div>
  `,
  styles: []
})
export class ToastComponent implements AfterViewInit {
  @Input() index;
  @Input() type;
  @Input() msg;

  ngAfterViewInit() {}

      constructor(private toaster: ToastService) {}

  closeToast() {
    this.toaster.destroyToast(this.index);
  }
}

toast.service.ts

import { ToastComponent } from './toast.component';
import { ToastModule } from './toast.module';
import {
  ComponentFactoryResolver,
  EmbeddedViewRef,
  ApplicationRef,
  ComponentRef,
  Injectable,
  Injector
} from '@angular/core';
@Injectable({
  providedIn: ToastModule
})
export class ToastService {
  toastComponentRef: ComponentRef<ToastComponent>;
  private compIndex = 0;

  constructor(
    private componentFactoryResolver: ComponentFactoryResolver,
    private appRef: ApplicationRef,
    private injector: Injector
  ) {}

  private createToast() {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(
      ToastComponent
    );
    const componentRef = componentFactory.create(this.injector);
    this.appRef.attachView(componentRef.hostView);

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

    this.toastComponentRef = componentRef;
  }

  private bindToastData(type: string, msg: string) {
    this.toastComponentRef.instance.index = this.compIndex++;
    this.toastComponentRef.instance.type = type;
    this.toastComponentRef.instance.msg = msg;
  }

  public destroyToast(index) {
    this.appRef.detachView(this.toastComponentRef.hostView);
    this.toastComponentRef.destroy();
  }

  public toast(type: string, msg: string) {
    this.createToast();
    this.bindToastData(type, msg);
  }
} 

app.component.ts

import { Component } from '@angular/core';
import { ToastService } from 'toast';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  constructor(private toast: ToastService) {
    // pushing toasts
    this.toast.toast('Error', 'Invalid Credentials');
    this.toast.toast('success', 'success info');
    this.toast.toast('warn', 'warn info');
  }
} 

toast.module.ts

import { NgModule } from '@angular/core';
import { ToastComponent } from './toast.component';

@NgModule({
  declarations: [ToastComponent],
  imports: [],
  exports: [ToastComponent],
  entryComponents: [ToastComponent]
})
export class ToastModule {} 

I am expecting to delete the component that I am clicking on.

Base on this demo you can do like this

  private removeDialogComponentFromBody() {
    this.appRef.detachView(this.dialogComponentRef.hostView);
    this.dialogComponentRef.destroy();
  }

And my blog show how I'm destroy dynamic component

import {
    ComponentFactoryResolver,
    Injectable,
    ComponentRef
} from "@angular/core";
@Injectable()
export class ComponentFactoryService {
    private componentRef: ComponentRef<any>;
    constructor(private componentFactoryResolver: ComponentFactoryResolver) {}

    createComponent(
        componentInstance: any,
        viewContainer: any
    ): ComponentRef<any> {
        const componentFactory = this.componentFactoryResolver.resolveComponentFactory(
            componentInstance
        );
        const viewContainerRef = viewContainer.viewContainerRef;
        viewContainerRef.clear();
        this.componentRef = viewContainerRef.createComponent(componentFactory);
        return this.componentRef;
    }

    destroyComponent() {
        if (this.componentRef) {
            this.componentRef.destroy();
        }
    }
}

You have to remember that the components .html files are not what's actually being shown, it's used by the Angular View Engine (Renderer2) to create the real DOM displayed by the browser.

As such, if you want to "destroy" a component from view, you can do this so Renderer2 will remove it from view (the DOM):

<div *ngIf="false">
    <uni-toast></uni-toast>
</div>

A solution to display a toaster and hide it programmatically:

  • Use the Toaster Service to hold a boolean saying the toaster should be displayed or not
  • The component presenting your toaster component should use this boolean to display it conditionally with a ngIf

Your service can also hold the text message, icon, and you can push events using observables.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM