Html 在 Angular 中动态添加和移除组件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/44939878/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Dynamically ADDING and REMOVING Components in Angular
提问by Marcellino Corleone
The current official docs only shows how to dynamically changecomponents withinan <ng-template>
tag. https://angular.io/guide/dynamic-component-loader
当前的官方文档只展示了如何动态更改标签内的组件<ng-template>
。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:
我想要实现的是,假设我有 3 个组件:header
, section
, 和footer
以下选择器:
<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
再有6个按钮,这将增加或删除每个组件:Add Header
,Add Section
,和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:
当我单击 时Add Header
,该页面将添加<app-header>
到呈现它的页面中,因此该页面将包含:
<app-header>
And then if I click Add Section
twice, the page will now contain:
然后如果我点击Add Section
两次,页面现在将包含:
<app-header>
<app-section>
<app-section>
And if I click Add Footer
, the page will now contain all these components:
如果我单击Add Footer
,该页面现在将包含所有这些组件:
<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.
是否有可能在 Angular 中实现这一点?请注意,这ngFor
不是我正在寻找的解决方案,因为它只允许向页面添加相同的组件,而不是不同的组件。
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.
编辑:ngIf
并且ngFor
不是我正在寻找的解决方案,因为模板已经预先确定。我所寻找的是类似的堆栈component
秒或一array
的component
S其中,我们可以添加,删除和更改的任何索引array
容易。
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:
编辑 2:为了更清楚,让我们举另一个例子来说明为什么ngFor
不起作用。假设我们有以下组件:
<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.
现在出现了一个新组件 ,<app-description>
用户希望将其插入到 和 之间<app-editor>
。ngFor
只有当我想一遍又一遍地循环一个相同的组件时才有效。但是对于不同的组件,ngFor
这里失败了。
回答by Ash Belmokadem
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.
您可以通过使用 动态创建组件ComponentFactoryResolver
然后将它们注入到ViewContainerRef
. 动态执行此操作的一种方法是将组件的类作为将创建和注入组件的函数的参数传递。
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]
).
如果您希望稍后更轻松地移除组件,您可以在局部变量中跟踪它们,请参阅
this.components
。或者,您可以循环遍历ViewContainerRef
.您必须将您的组件注册为入口组件。在您的模块定义中,将您的组件注册为 entryComponent (
entryComponents: [DraggableComponent]
)。
Running example: https://plnkr.co/edit/mrXtE1ICw5yeIUke7wl5
运行示例:https: //plnkr.co/edit/mrXtE1ICw5yeIUke7wl5
For more information: https://angular.io/guide/dynamic-component-loader
回答by WasiF
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
父组件
import { ComponentRef, ComponentFactoryResolver, ViewContainerRef, ViewChild, Component } from "@angular/core";
@Component({
selector: 'parent',
template: `
<button type="button" (click)="createComponent()">
Create Child
</button>
<div>
<ng-template #viewContainerRef></ng-template>
</div>
`
})
export class ParentComponent implements myinterface {
@ViewChild('viewContainerRef', { read: ViewContainerRef }) VCR: ViewContainerRef;
//manually indexing the child components for better removal
//although there is by-default indexing but it is being avoid for now
//so index is a unique property here to identify each component individually.
index: number = 0;
// to store references of dynamically created components
componentsReferences = [];
constructor(private CFR: ComponentFactoryResolver) {
}
createComponent() {
let componentFactory = this.CFR.resolveComponentFactory(ChildComponent);
let componentRef: ComponentRef<ChildComponent> = this.VCR.createComponent(componentFactory);
let currentComponent = componentRef.instance;
currentComponent.selfRef = currentComponent;
currentComponent.index = ++this.index;
// prividing parent Component reference to get access to parent class methods
currentComponent.compInteraction = this;
// add reference for newly created component
this.componentsReferences.push(componentRef);
}
remove(index: number) {
if (this.VCR.length < 1)
return;
let componentRef = this.componentsReferences.filter(x => x.instance.index == index)[0];
let component: ChildComponent = <ChildComponent>componentRef.instance;
let vcrIndex: number = this.VCR.indexOf(componentRef)
// removing component from container
this.VCR.remove(vcrIndex);
this.componentsReferences = this.componentsReferences.filter(x => x.instance.index !== index);
}
}
Child Component
子组件
@Component({
selector: 'child',
template: `
<div>
<h1 (click)="removeMe(index)">I am a Child, click to Remove</h1>
</div>
`
})
export class ChildComponent {
public index: number;
public selfRef: ChildComponent;
//interface for Parent-Child interaction
public compInteraction: myinterface;
constructor() {
}
removeMe(index) {
this.compInteraction.remove(index)
}
}
// Interface
export interface myinterface {
remove(index: number);
}
add references to the app.module.ts
添加对app.module.ts 的引用
@NgModule({
declarations: [
ParentComponent,
ChildComponent
],
imports: [
//if using routing then add like so
RouterModule.forRoot([
{ path: '', component: ParentComponent }
]),
],
entryComponents: [
ChildComponent,
],