Dynamically ADDING and REMOVING Components in Angular

Solution 1:

What you're trying to achieve can be done by creating components dynamically using the ComponentFactoryResolver and then injecting them into a ViewContainerRef. One way to do this dynamically is by passing the class of the component as an argument of your function that will create and inject the component.

See example below:

import {
  Component,
  ComponentFactoryResolver, Type,
  ViewChild,
  ViewContainerRef
} from '@angular/core';

// Example component (can be any component e.g. app-header app-section)
import { DraggableComponent } from './components/draggable/draggable.component';

@Component({
  selector: 'app-root',
  template: `
    <!-- Pass the component class as an argument to add and remove based on the component class -->
    <button (click)="addComponent(draggableComponentClass)">Add</button>
    <button (click)="removeComponent(draggableComponentClass)">Remove</button>

    <div>
      <!-- Use ng-template to ensure that the generated components end up in the right place -->
      <ng-template #container>

      </ng-template>
    </div>

  `
})
export class AppComponent {
  @ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef;

  // Keep track of list of generated components for removal purposes
  components = [];

  // Expose class so that it can be used in the template
  draggableComponentClass = DraggableComponent;

  constructor(private componentFactoryResolver: ComponentFactoryResolver) {
  }

  addComponent(componentClass: Type<any>) {
    // Create component dynamically inside the ng-template
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(componentClass);
    const component = this.container.createComponent(componentFactory);

    // Push the component so that we can keep track of which components are created
    this.components.push(component);
  }

  removeComponent(componentClass: Type<any>) {
    // Find the component
    const component = this.components.find((component) => component.instance instanceof componentClass);
    const componentIndex = this.components.indexOf(component);

    if (componentIndex !== -1) {
      // Remove component from both view and array
      this.container.remove(this.container.indexOf(component));
      this.components.splice(componentIndex, 1);
    }
  }
}

Notes:

  1. If you want to make it easier to remove the components later on, you can keep track of them in a local variable, see this.components. Alternatively you can loop over all the elements inside the ViewContainerRef.

  2. You have to register your component as an entry component. In your module definition register your component as an entryComponent (entryComponents: [DraggableComponent]).

Running example: https://plnkr.co/edit/mrXtE1ICw5yeIUke7wl5

For more information: https://angular.io/guide/dynamic-component-loader

Solution 2:

I have created a demo to show the dynamic add and remove process. Parent component creates the child components dynamically and removes them.

Click for demo

Parent Component

// .ts
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
    );
  }
}

// .html
<button type="button" (click)="createComponent()">
    I am Parent, Create Child
</button>
<div>
    <ng-template #viewContainerRef></ng-template>
</div>

Child Component

// .ts
export class ChildComponent {

  public unique_key: number;
  public parentRef: ParentComponent;

  constructor() {
  }

  remove_me() {
    console.log(this.unique_key)
    this.parentRef.remove(this.unique_key)
  }
}

// .html
<button (click)="remove_me()">I am a Child {{unique_key}}, click to Remove</button>