You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular 5响应式表单中修改对象副本致原对象变更问题

问题:修改Angular响应式表单value副本时,原对象居然也跟着变了?

我在Angular里用响应式表单构建了一个任务表单,提交前想复制表单的value对象,修改副本再发给服务器,但发现修改副本时原表单的value也跟着变了,这是怎么回事?

表单构建代码:

newTask: FormGroup;
this.newTask = this.formBuilder.group({
  partner: [null, [Validators.required]],
  title: [null, [Validators.required, Validators.minLength(5), Validators.maxLength(50)]],
  description: [null, [Validators.minLength(10), Validators.maxLength(120)]],
  responsible: [null, [Validators.required]],
  type: [null, [Validators.required]],
  priority: [null, [Validators.required]],
  status: [null, [Validators.required]],
  history: this.formBuilder.array([])
})

添加/移除history数组元素的代码:

createAction(action?) {
  return this.formBuilder.group({
    title: [action ? action.title : undefined, [Validators.required, Validators.minLength(5), Validators.maxLength(50)]],
    description: [action ? action.description : undefined, [Validators.required, Validators.minLength(10), Validators.maxLength(200)]],
    resolveAt: [action ? action.resolveAt : undefined],
    result: [action ? action.response : undefined, [Validators.minLength(10), Validators.maxLength(200)]],
    resolvedAt: [action ? action.resolvedAt : undefined],
    status: [action ? action.status : undefined, [Validators.required]],
    relatedPerson: [action ? action.relatedPerson : undefined]
  })
}

addAction(action?): void {
  const history = this.newTask.get('history') as FormArray;
  history.push(this.createAction(action || initialActionState));
}

removeAction(i: number): void {
  const history = this.newTask.get('history') as FormArray;
  history.removeAt(i);
}

提交时的代码(问题出在这里):

let cleanVar = {...this.newTask.value} // 也试过Object.assign({}, this.newTask.value),结果一样
cleanVar.history.forEach(item => {
  item.status = item.status.string;
  if (item.relatedPerson) item.relatedPerson = item.relatedPerson._id;
})
console.log(this.newTask.value); // 发现原对象也被修改了!

原因分析:浅拷贝的坑!

兄弟,这个问题我之前也踩过!和Angular响应式表单本身没啥关系,核心是你用的浅拷贝搞的鬼😅

不管是扩展运算符{...obj}还是Object.assign({}, obj),都属于浅拷贝——它们只会复制对象的第一层属性,但对于嵌套的对象(比如你这里的history数组里的每一项),拷贝的其实是引用,而不是创建新的独立对象。

换句话说,cleanVar.history和this.newTask.value.history指向的是同一个数组,数组里的每个item也都是原表单value里对应对象的引用。所以当你修改cleanVar.history里的item.status或者item.relatedPerson时,本质上是在修改原对象的属性,自然会影响到this.newTask.value。


解决方案:用深拷贝搞定它

要解决这个问题,你需要对表单的value进行深拷贝,确保所有嵌套对象都被复制成独立的新对象。这里有几种常用的方法:

方法1:JSON序列化反序列化(最简单快捷)

这是最省事的方式,适合表单值里没有特殊类型(比如Date、Function)的场景:

let cleanVar = JSON.parse(JSON.stringify(this.newTask.value));

⚠️ 注意:如果你的表单里有Date对象,这种方法会把它转成字符串,需要后续手动转回来;如果有函数、undefined等,会被直接忽略。

方法2:用第三方库做深拷贝(最可靠)

如果需要处理特殊类型,推荐用Lodash的cloneDeep方法(需要先安装lodash:npm i lodash):

import cloneDeep from 'lodash/cloneDeep';

let cleanVar = cloneDeep(this.newTask.value);

这个方法能完美处理各种类型的嵌套对象,省心又靠谱。

方法3:手动遍历嵌套结构(自定义性最强)

如果你的表单结构固定,也可以手动遍历每一层进行拷贝,还能顺便完成修改,一举两得:

let cleanVar = {...this.newTask.value};
// 手动深拷贝history数组里的每一项,同时完成修改
cleanVar.history = cleanVar.history.map(item => ({
  ...item,
  status: item.status.string,
  relatedPerson: item.relatedPerson ? item.relatedPerson._id : item.relatedPerson
}));

这种方式不需要额外依赖,还能精准控制每一步的修改,适合有特定需求的场景。


内容的提问来源于stack exchange,提问作者Felipe Micali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:20:18