Search code examples
angularindexingdynamiccomponents

Add and remove dynamic component Angular


I have this code in Angular 12 and it works perfectly. The problem comes when changing the version from 12 to 16.

import {
  ComponentRef,
  ComponentFactoryResolver,
  ViewContainerRef,
  ViewChild,
  Component,
ViewRef
} from "@angular/core";
import { ChildComponent } from "../child/child.component";

@Component({
  selector: "app-parent",
  templateUrl: "./parent.component.html",
  styleUrls: ["./parent.component.css"]
})
export class ParentComponent {
  @ViewChild("viewContainerRef", { read: ViewContainerRef })
  VCR: ViewContainerRef;

  child_unique_key: number = 0;
  componentsReferences = Array<ComponentRef<ChildComponent>>()

  constructor(private CFR: ComponentFactoryResolver) {}

  createComponent() {
    let componentFactory = this.CFR.resolveComponentFactory(ChildComponent);

    let childComponentRef = this.VCR.createComponent(componentFactory);

    let childComponent = childComponentRef.instance;
    childComponent.unique_key = ++this.child_unique_key;
    childComponent.parentRef = this;

    // add reference for newly created component
    this.componentsReferences.push(childComponentRef);
  }

  remove(key: number) {
    if (this.VCR.length < 1) return;

    let componentRef = this.componentsReferences.filter(
      x => x.instance.unique_key == key
    )[0];

    let vcrIndex: number = this.VCR.indexOf(componentRef as any);

    // removing component from container
    this.VCR.remove(vcrIndex);

    // removing component from the list
    this.componentsReferences = this.componentsReferences.filter(
      x => x.instance.unique_key !== key
    );
  }
}

Example: https://stackblitz.com/edit/add-or-remove-dynamic-component?file=src%2Fapp%2Fparent%2Fparent.component.ts

The problem in Angular 16 is that vcrIndex is always -1 when not found.

let vcrIndex: number = this.VCR.indexOf(componentRef as any);

// removing component from container
this.VCR.remove(vcrIndex);

Any suggestions or how can I fix this without having to modify the way dynamic components are created? I have tried several things such as adding an index or an identifier but I still have not been able to solve the problem.

Thx

I have looked to see if the VCR structure is different and I could add an identifier and search for it manually but I couldn't.


Solution

  • Not sure why exactly it worked before, guess it was just a coincidence. this.VCR.indexOf actually expects ViewRef and you pass a ComponentRef there casting it to any.

    The fix is quite simple, you just need to pass componentRef.hostView to the VCR.indexOf:

    let vcrIndex: number = this.VCR.indexOf(componentRef.hostView);
    

    Here is a working demo using Angular 17.


    Just in case, ComponentFactoryResolver was deprecated long time ago (v13), and you can pass component type directly to the this.VCR.createComponent:

    let childComponentRef = this.VCR.createComponent(ChildComponent);