在Angular中动态添加和删除组件
在Angular中动态添加和删除组件
官方文档目前只展示了如何在
标签内动态更改组件。我想要实现的是,假设我有三个组件:header
、section
和footer
,它们的选择器分别是:
、
和
。然后有6个按钮,用于添加或移除每个组件:Add Header
、Add Section
和Add Footer
。当我点击Add Header
时,页面将会添加
,所以页面会包含:
如果我连续点击Add Section
两次,页面现在会包含:
如果我点击Add Footer
,页面现在将包含所有这些组件:
在Angular中是否能实现这个功能?请注意,ngFor
不是我要找的解决方案,因为它只能添加相同的组件,而不能添加不同的组件到页面上。
编辑:ngIf
和ngFor
也不是我要找的解决方案,因为模板已经确定。我要找的是一种类似于一堆组件或一个组件数组的方式,可以轻松地添加、删除和更改数组的任何索引。
编辑 2:为了更清楚,让我们举一个其他例子来说明为什么ngFor
不起作用。假设我们有以下组件:
现在新加入一个组件
,用户希望将其插入在
和
之间。但ngFor
只能在相同组件上循环,对于不同的组件,ngFor
无法实现这个功能。
在Angular中,动态地添加和删除组件是一个常见的需求。原因是我们可能需要根据用户的操作或其他条件来动态地添加或删除一些组件,以实现更灵活和交互性的界面。下面是解决这个问题的一种方法:
对于Angular v13或以上版本,可以通过以下步骤来动态地添加组件:
1. 在父组件的HTML模板中定义一个ng-template,并为其添加一个引用,如下所示:
2. 在父组件的TypeScript文件中,使用ViewChild装饰器来获取对ng-template的引用,如下所示:
@ViewChild("viewContainerRef", { read: ViewContainerRef }) vcr!: ViewContainerRef;
3. 在添加组件的方法中,使用createComponent方法来创建组件,并将其添加到ng-template中,如下所示:
ref!: ComponentRef; addChild() { this.ref = this.vcr.createComponent(YourChildComponent); }
4. 在删除组件的方法中,使用indexOf方法来获取要删除的组件在ng-template中的索引,并使用remove方法将其从ng-template中移除,如下所示:
removeChild() { const index = this.vcr.indexOf(this.ref.hostView); if (index != -1) { this.vcr.remove(index); } }
对于Angular v12或以下版本,可以按照以下步骤来动态地添加和删除组件:
1. 在父组件的TypeScript文件中,创建一个数组来保存动态添加的组件的引用,如下所示:
componentsReferences = Array>();
2. 在创建组件的方法中,使用ComponentFactoryResolver来解析要创建的组件,并使用createComponent方法来创建组件实例,如下所示:
constructor(private CFR: ComponentFactoryResolver) {} createComponent() { let componentFactory = this.CFR.resolveComponentFactory(ChildComponent); let childComponentRef = this.VCR.createComponent(componentFactory); // 将引用添加到数组中 this.componentsReferences.push(childComponentRef); }
3. 在删除组件的方法中,使用indexOf方法来获取要删除的组件在数组中的索引,并使用remove方法将其从数组中移除,如下所示:
remove(key: number) { let componentRef = this.componentsReferences.filter(x => x.instance.unique_key == key)[0]; let vcrIndex: number = this.VCR.indexOf(componentRef as any); // 从容器中移除组件 this.VCR.remove(vcrIndex); // 从数组中移除组件的引用 this.componentsReferences = this.componentsReferences.filter(x => x.instance.unique_key !== key); }
4. 在父组件的HTML模板中,使用按钮来触发创建和删除组件的方法,如下所示:
以上就是在Angular中动态添加和删除组件的原因和解决方法。无论是在Angular v13或以上版本还是在Angular v12或以下版本,我们都可以使用相应的方法来实现动态操作组件的需求。
Dynamically ADDING and REMOVING Components in Angular(在Angular中动态添加和删除组件)
在Angular中,有时候我们需要动态地添加和删除组件。这种需求可以通过使用ComponentFactoryResolver
动态创建组件,并将它们注入到ViewContainerRef
中来实现。一种动态实现的方法是将组件的类作为参数传递给一个函数,该函数将创建并注入组件。
下面是一个示例:
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: `` }) 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) { // 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 ) { // 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); } } }
注意:
1. 如果想要更容易地删除组件,可以在本地变量中跟踪它们,如this.components
所示。或者可以遍历ViewContainerRef
中的所有元素。
2. 必须将组件注册为入口组件。在模块定义中,将组件注册为入口组件(entryComponents: [DraggableComponent]
)。
运行示例:
https://plnkr.co/edit/mrXtE1ICw5yeIUke7wl5
更多信息:
https://angular.io/guide/dynamic-component-loader
感谢!这可能是我正在寻找的解决方案,但我还没有尝试过。您能提供一个Plunker吗?
我已经在答案中添加了一个Plunker。请参见https://plnkr.co/edit/mrXtE1ICw5yeIUke7wl5。希望对您有所帮助。
我编辑了Plunker,包括不同的组件(foo和bar),并且它运行良好!非常感谢!
很棒的例子和代码。我正在寻找这个。我只想建议将"const component = this.components.find…"修改为"const component = this.components.reverse().find…",以删除最后插入的组件。或者为它们添加id以跟踪特定的组件。
这里还有一个关于这个的教程:courses.ultimateangular.com/courses/angular-pro/lectures/…
嗨,非常感谢您的答案。您或其他人知道如何更新此示例以支持绑定吗?例如,如果我希望DraggableComponent
绑定到ng-template
上的let
属性,您将如何实现呢?如果要手动编写代码,它将如下所示:<ng-template #ref let-item> <my-component [myBinding]=item></my-component> </ng-template>
ComponentFactoryResolver
已经被弃用。