html - Dynamiskt LÄGGA TILL och TA BORT Komponenter i Angular
De nuvarande officiella dokumenten visar endast hur man dynamiskt byter komponenter inom en <ng-template>-tagg. https://angular.io/guide/dynamic-component-loader
Det jag vill uppnå är, låt oss säga att jag har 3 komponenter: header, section och footer med följande selektorer:
<app-header>
<app-section>
<app-footer>
Och sedan finns det 6 knappar som kommer att lägga till eller ta bort varje komponent: Lägg till rubrik, Lägg till sektion, och Lägg till sidfot
och när jag klickar på Lägg till rubrik, kommer sidan att lägga till <app-header> på sidan som renderar den, så sidan kommer att innehålla:
<app-header>
Och sedan, om jag klickar på Lägg till sektion två gånger, kommer sidan nu att innehålla:
<app-header>
<app-section>
<app-section>
Och om jag klickar på Lägg till sidfot kommer sidan nu att innehålla alla dessa komponenter:
<app-header>
<app-section>
<app-section>
<app-footer>
Är det möjligt att uppnå detta i Angular? Observera att ngFor inte är lösningen jag letar efter, eftersom det endast tillåter att lägga till samma komponenter, inte olika komponenter på en sida.
REDIGERA: ngIf och ngFor är inte lösningen jag letar efter eftersom mallarna redan är förutbestämda. Det jag letar efter är något som en stack av components eller en array av components där vi enkelt kan lägga till, ta bort och ändra vilken index som helst i array.
REDIGERA 2: För att göra det tydligare, låt oss ta ett annat exempel på varför ngFor inte fungerar. Säg att vi har följande komponenter:
<app-header>
<app-introduction>
<app-camera>
<app-editor>
<app-footer>
Nu kommer en ny komponent, <app-description>, som användaren vill infoga mellan och <app-editor>. ngFor fungerar bara om det finns en och samma komponent som jag vill loopa över och över igen. Men för olika komponenter misslyckas ngFor här.
Svar #1
Det du försöker uppnå kan göras genom att skapa komponenter dynamiskt med ComponentFactoryResolver och sedan injicera dem i en ViewContainerRef. Ett sätt att göra detta dynamiskt är att skicka klassnamnet på komponenten som ett argument till din funktion som kommer att skapa och injicera komponenten.
Se exempel nedan:
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);
}
}
}
Anteckningar:
Om du vill göra det enklare att ta bort komponenterna senare kan du hålla reda på dem i en lokal variabel, se
this.components. Alternativt kan du loopa över alla element inutiViewContainerRef.Du måste registrera din komponent som en ingångskomponent. I din moduldefinition registrerar du din komponent som en entryComponent (
entryComponents: [DraggableComponent]).
Körande exempel: https://plnkr.co/edit/mrXtE1ICw5yeIUke7wl5
För mer information: https://angular.io/guide/dynamic-component-loader
Svar #2
Angular v13 eller högre - enkelt sätt att lägga till dynamiska komponenter i DOM
parent.component.html<ng-template #viewContainerRef></ng-template>
parent.component.ts@ViewChild("viewContainerRef", { read: ViewContainerRef }) vcr!: ViewContainerRef;
ref!: ComponentRef<YourChildComponent>
addChild() {
this.ref = this.vcr.createComponent(YourChildComponent)
}
removeChild() {
const index = this.vcr.indexOf(this.ref.hostView)
if (index != -1) this.vcr.remove(index)
}
Angular v12 eller tidigare
Jag har skapat en demo för att visa processen med dynamisk tilläggning och borttagning. Föräldrakomponenten skapar underkomponenterna dynamiskt och tar bort dem.
Klicka för demo
Föräldrakomponent
// .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>
Barnkomponent
// .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>
