Angular 13+动态增删组件问题:删除子组件时索引为-1无法删除
Angular 13+ 动态增删子组件异常问题:仅能删除最后一个子组件,后续删除hostView索引返回-1
在Angular 13+中实现动态增删子组件时遇到以下问题:父组件仅能删除最后一个创建的子组件,后续执行删除操作时,hostView的索引返回-1,无法从视图中移除目标子组件。曾尝试过旧版ComponentFactoryResolver的实现方式,但希望基于Angular新版本的API解决该问题。
相关代码
子组件
子组件视图(child.component.html)
<button (click)="remove_me()">I am a Child {{unique_key}}, click to Remove</button>
子组件类(child.component.ts)
import { Component } from '@angular/core'; import { ParentComponent } from '../parent/parent.component'; @Component({ selector: 'app-child', templateUrl: './child.component.html', styleUrls: ['./child.component.css'], }) export class ChildComponent { public unique_key: number; public parentRef: ParentComponent; constructor() {} remove_me() { console.log(this.unique_key); this.parentRef.remove(this.unique_key); } }
父组件
父组件视图(parent.component.html)
<button type="button" (click)="AddChild()"> I am Parent, Click to create Child </button> <div> <ng-template #viewContainerRef></ng-template> </div>
父组件类(parent.component.ts)
import { ComponentRef, ViewContainerRef, ViewChild, Component, } from '@angular/core'; import { ChildComponent } from '../child/child.component'; @Component({ selector: 'app-parent', templateUrl: './parent.component.html', styleUrls: ['./parent.component.css'], }) export class ParentComponent { @ViewChild('viewContainerRef', { read: ViewContainerRef }) vcr!: ViewContainerRef; ref!: ComponentRef<ChildComponent>; child_unique_key: number = 0; componentsReferences = Array<ComponentRef<ChildComponent>>(); constructor() {} AddChild() { this.ref = this.vcr.createComponent(ChildComponent); let childComponent = this.ref.instance; childComponent.unique_key = ++this.child_unique_key; childComponent.parentRef = this; } remove(key: number) { const index = this.vcr.indexOf(this.ref.hostView); console.log(index); if (index != -1) { this.vcr.remove(index); } // removing component from the list this.componentsReferences = this.componentsReferences.filter( (x) => x.instance.unique_key !== key ); } }
问题原因
- 全局变量
ref覆盖问题:父组件中的ref变量为全局变量,每次调用AddChild时都会被新创建的子组件引用覆盖,导致其永远指向最后一个创建的子组件。当删除非最后一个子组件时,用这个ref的hostView去查询索引,自然无法找到目标组件,返回-1。 - 组件引用数组未维护:
componentsReferences数组在创建子组件时没有将新的ComponentRef添加进去,导致后续的过滤操作无法正确移除对应组件引用。
解决方案
修改父组件的AddChild和remove方法,正确维护子组件引用数组,并根据目标unique_key找到对应的组件引用进行删除:
修改后的父组件类代码
import { ComponentRef, ViewContainerRef, ViewChild, Component, } from '@angular/core'; import { ChildComponent } from '../child/child.component'; @Component({ selector: 'app-parent', templateUrl: './parent.component.html', styleUrls: ['./parent.component.css'], }) export class ParentComponent { @ViewChild('viewContainerRef', { read: ViewContainerRef }) vcr!: ViewContainerRef; child_unique_key: number = 0; componentsReferences = Array<ComponentRef<ChildComponent>>(); constructor() {} AddChild() { // 改用局部变量存储新组件引用 const ref = this.vcr.createComponent(ChildComponent); let childComponent = ref.instance; childComponent.unique_key = ++this.child_unique_key; childComponent.parentRef = this; // 将新组件引用添加到数组中 this.componentsReferences.push(ref); } remove(key: number) { // 根据unique_key找到目标组件引用 const targetRef = this.componentsReferences.find(x => x.instance.unique_key === key); if (targetRef) { // 获取目标组件在容器中的索引并移除 const index = this.vcr.indexOf(targetRef.hostView); if (index !== -1) { this.vcr.remove(index); } // 也可以直接调用destroy方法销毁组件(效果等同于remove) // targetRef.destroy(); // 从引用数组中移除目标组件 this.componentsReferences = this.componentsReferences.filter(x => x !== targetRef); } } }
关键修改点
- 移除全局
ref变量,改用局部变量存储新创建的组件引用,并将其添加到componentsReferences数组中。 - 在
remove方法中,通过unique_key从数组中找到对应的ComponentRef,再基于该引用获取索引并执行删除操作。 - 可选:直接调用
targetRef.destroy()可以同时销毁组件并从视图容器中移除,无需手动调用vcr.remove(index)。
内容的提问来源于stack exchange,提问作者mr fabilos
相关产品推荐
相关产品推荐

