Dynamically Adding and Removing Components in Angular

Dynamically ADDING and REMOVING Components in Angular

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

How do I remove a component dynamically in angular 9?

You have such inconsistency because you're using ViewContainerRef API in a wrong way.

Here's the signature of indexOf method:

abstract indexOf(viewRef: ViewRef): number;

This signature never changed during Angular updates.

The stackblitz you're referring to uses Angular 6 version that leverage ViewEngine under the hood but in your code you're using Angular 9 and above version where Ivy compiler comes into play.

In stackblitz you have:

this.VCR.indexOf(componentRef as any);

Meaning that you're passing ComponentRef instance not ViewRef instance. It works by accident because indexOf method looks like:

ViewContainerRef_.prototype.indexOf = function (viewRef) {
return this._embeddedViews.indexOf(viewRef._view);
};

and

ComponentRef._view === ViewRef._view in ViewEngine.

You should be passing ViewRef instance instead:

this.VCR.indexOf(componentRef.hostView)

Forked Stackblitz

The demo works with Ivy(your particular case) but it will also work in ViewEngine.

Dynamically creating and removing components in Angular

I would do what the commenter Ricardo suggested and use dependency injection.

Somewhere inside component A add a method that will be called by each child component B in their ngOnInit...

 componentList : any[] = [];

addChild(component: any) {
this.componentList.push(component)
}

Then inside the child components, inject the parent into their constructor and call the add method in the ngOnInit so the parent maintains an array of every instance of component B

 constructor( private parent: ComponentA) {}

ngOnInit() { this.parent.addChild(this); }

Now from any B component you could access the parent components public 'componentList' property to see all the created instances. Don't forget to import ComponentA inside the ComponentB files

Delete Dynamically Created Components, one by one?

In your second project, you are calling this.selfref.destroy() inside the ngOnDestroy life-cycle callback. This creates an infinite loop because ngOnDestroy is triggered by the call to this.selfref.destroy(). You shouldn't need to explicitly handle the ComponentRef like that. It is a reference, after all.

If you remove the call to this.selfref.destroy(), your second project works like a charm.

Remove dynamically created element by id in angular component

You can add a #local reference" to a ng-container or a div that contains your element, and then in your code, with @ViewChild link the reference, and finally delete it with:

@ViewChild('yourLocalReferenceHTMLname') private yourLocalReferenceHTMLname: ElementRef;

this.yourLocalReferenceHTMLname.nativeElement.remove();

Anyway, try not to use elementRef [From the Angular Documents]:

Use elementRef API as the last resort when direct access to DOM is needed. Use templating and data-binding provided by Angular instead.
Alternatively, you can use Renderer2, which provides API that can safely be used even when direct access to native elements is not supported.

Relying on direct DOM access creates tight coupling between your application and rendering layers which will make it impossible to separate the two and deploy your application into a web worker.

Literally, from this render2 article HERE:

Why not ElementRef?

We can use the nativeElement property of the ElelemtRef to manipulate the DOM.The nativeElement Property contains the reference to the underlying DOM object. This gives us direct access to the DOM, bypassing the Angular.

There is nothing wrong with using it, but it is not advisable for the following reasons:

Angular keeps the Component & the view in Sync using Templates, data binding & change detection, etc. All of them are bypassed when we update the DOM Directly.

DOM Manipulation works only in Browser. You will not able to use the App in other platforms like in a web worker, in Server (Server-side rendering), or in a Desktop, or in the mobile app, etc where there is no browser.

The DOM APIs do not sanitize the data. Hence it is possible to inject a script, thereby, opening our app an easy target for the XSS injection attack.


Related Topics



Leave a reply



Submit