Angular 4模板驱动表单:数组splice后*ngFor不更新表单输入name
这个问题我之前也碰到过,本质是Angular的*ngFor跟踪机制导致的,我来给你拆解下原因和解决办法:
问题根源
Angular的*ngFor默认是用元素索引或者对象引用来识别列表项的。当你用splice删除数组中的某个元素后,后面的元素索引虽然发生了变化,但Angular并没有重新渲染这些对应的<input>元素——它觉得这些元素还是原来的那些,所以绑定的name="radioBtn_{{i}}"里的索引值没更新,最终表单值里就保留了旧的name属性(比如你例子里的radioBtn_2、radioBtn_3,其实对应的元素已经变成了索引1、2)。
推荐解决方案:使用trackBy函数
这是最规范的解决方式,让Angular根据每个选项的唯一标识来跟踪元素,而不是依赖易变的索引。
步骤1:给每个选项添加唯一标识
修改你的addOption方法,给每个新添加的选项分配一个自增的唯一ID:
// 组件类里新增一个计数器 private optionIdCounter = 0; addOption(){ this.f._submitted = false; this.questions.options.push({ logic : {}, id: this.optionIdCounter++ // 给每个选项加唯一ID }); }
步骤2:添加trackBy函数
在组件类里定义一个trackByOption函数,用来告诉Angular怎么识别唯一的选项:
trackByOption(index: number, option: any): number { return option.id; // 返回选项的唯一ID作为跟踪标识 }
步骤3:修改模板的*ngFor
给*ngFor加上trackBy绑定:
<div class="row p-t-20" *ngFor="let multiple_option of questions.options; let i=index; trackBy: trackByOption">
这样操作后,当你删除某个选项时,Angular会正确识别哪些元素需要重新渲染,对应的name属性会根据新的索引更新,表单值里的name也会和当前元素的索引对应上。
临时应急方案:强制数组重新生成引用
如果不想加唯一ID和trackBy,也可以通过创建数组的新引用来强制Angular重新渲染整个列表(不推荐大量数据场景,性能略差):
修改removeOption方法:
removeOption(index){ this.questions.options.splice(index,1); // 重新赋值数组,生成新引用触发重新渲染 this.questions.options = [...this.questions.options]; console.log(this.questions.options); }
验证效果
修改完成后,删除选项再查看表单值,就能看到radioBtn_*的编号和当前元素的索引完全对应,不会再出现旧的name残留了。
内容的提问来源于stack exchange,提问作者shashank chawan

