I can dynamically create a number of components and I want to be able to delete each one separately by clicking a button on each component. I have a working demonstration project which I kept as simple as possible. Please look at my stackblitz. You can create a number of components by clicking on the create button multiple times. That works the way I want it to work. But, I was hoping that there was an easier way. Here is app.component.ts
import {
Component,
ViewChild,
ComponentFactoryResolver,
ViewContainerRef,
ComponentRef,
OnDestroy
} from "@angular/core";
import { DynamicComponent } from "./dynamic/dynamic.component";
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnDestroy {
private _count = 0;
componentRef: ComponentRef<DynamicComponent>;
dynamics: Map<string, ComponentRef<DynamicComponent>> = new Map();
@ViewChild("viewcontainer", { read: ViewContainerRef, static: false })
viewContainerRef: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver) {}
createComponent() {
this._count++;
const factory = this.resolver.resolveComponentFactory(DynamicComponent);
this.componentRef = this.viewContainerRef.createComponent(factory);
this.componentRef.instance.name = " " + this._count;
this.dynamics.set(this.componentRef.instance.name, this.componentRef);
this.componentRef.instance.deleteName.subscribe(
name => this.destroyComponent(name),
error => console.log(error)
);
}
destroyComponent(name: string) {
if (this.dynamics.has(name)) {
this.dynamics.get(name).destroy();
}
this.dynamics.delete(name);
}
ngOnDestroy() {
this.dynamics.forEach(
(value: ComponentRef<DynamicComponent>, key: string) => {
value.instance.deleteName.unsubscribe();
}
);
this.dynamics = null;
}
}
here is app.component.html
<button (click)="createComponent()">Create a Component</button>
<ng-container #viewcontainer></ng-container>
here is dynamic.component.ts
import { Component, Input, Output, EventEmitter } from "@angular/core";
@Component({
selector: "app-dynamic",
templateUrl: "./dynamic.component.html",
styleUrls: ["./dynamic.component.css"]
})
export class DynamicComponent {
@Input() name: string;
@Output() deleteName = new EventEmitter<string>();
constructor() {}
delete() {
this.deleteName.emit(this.name);
}
}
here is dynamic.component.html
<div>
{{ name }} <button (click)="delete()">Delete</button>
</div>
I added the following line to app.module.ts
entryComponents: [DynamicComponent]
My question: Is there an easier way? Could @ViewChildren be used? Something like?
@ViewChildren(DynamicComponent) dynamics: QueryList<DynamicComponent>;
EDIT: After a good night's sleep, I have another idea. Why not have each dynamic component hold a reference to its self. Please look at my second stackblitz . That project doesn't use a map to hold all the ComponentRefs of all the dynamics. I just save a ComponentRef in each DynamicComponent. But, it throws errors.
DynamicComponent_Host.ngfactory.js? [sm]:1 ERROR RangeError: Maximum call stack size exceeded
at callWithDebugContext (services.ts:636)
at Object.debugDestroyView [as destroyView] (services.ts:355)
at ViewRef_.destroy (refs.ts:284)
at ComponentRef_.destroy (refs.ts:122)
at DynamicComponent.ngOnDestroy (dynamic.component.ts:20)
at callProviderLifecycles (provider.ts:575)
at callElementProvidersLifecycles (provider.ts:541)
at callLifecycleHooksChildrenFirst (provider.ts:529)
at destroyView (view.ts:511)
at callWithDebugContext (services.ts:630)
I don't understand why my second project doesn't work. The first one(at the top here) works.
EDIT: I found How to let a component delete itself on a button click with in angular
I guess that would be a lot easier than working with ComponentFactoryResolver.
EDIT: I found a similar question and solution at How to destroy component created dynamically angular 8
EDIT: I just found What is the proper use of an EventEmitter? So, guess my first project misuses EventEmitter?