Issue
The current official docs only shows how to dynamically change components within an <ng-template>
tag. https://angular.io/guide/dynamic-component-loader
What I want to achieve is, let's say I have 3 components: header
, section
, and footer
with the following selectors:
<app-header>
<app-section>
<app-footer>
And then there are 6 buttons that will add or remove each component: Add Header
, Add Section
, and Add Footer
and when I click Add Header
, the page will add <app-header>
to the page that renders it, so the page will contain:
<app-header>
And then if I click Add Section
twice, the page will now contain:
<app-header>
<app-section>
<app-section>
And if I click Add Footer
, the page will now contain all these components:
<app-header>
<app-section>
<app-section>
<app-footer>
Is it possible to achieve this in Angular? Note that ngFor
is not the solution I'm looking for, as it only allows to add the same components, not different components to a page.
EDIT: ngIf
and ngFor
is not the solution I'm looking for as the templates are already predetermined. What I am looking for is something like a stack of component
s or an array
of component
s where we can add, remove, and change any index of the array
easily.
EDIT 2: To make it more clear, let's have another example of why ngFor
does not work. Let's say we have the following components:
<app-header>
<app-introduction>
<app-camera>
<app-editor>
<app-footer>
Now here comes a new component, <app-description>
, which the user wants to insert in between and <app-editor>
. ngFor
works only if there is one same component that I want to loop over and over. But for different components, ngFor
fails here.
Solution
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:
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 theViewContainerRef
.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
Answered By - Ash Belmokadem
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.