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

Angular表单提交时Array Push丢失字符串值问题排查

问题描述

我在开发Angular个人项目时遇到了表单提交的异常行为:

  • 定义了CheckItem接口,通过Service维护该类型的数组(从assets目录的JSON文件实例化)
  • 组件使用checkItemForm存储表单数据,在onSubmit()方法中更新对象并通过Service将其推入数组
  • 直接传入this.newCheckItem时,ID能正确保留,但name始终被清空为空字符串;传入新创建的指定name和id的对象时,行为完全符合预期

接口定义

export interface CheckItem {
    id: number;
    name: string;
}

Service代码

export class ChecksService {
  items: CheckItem[] = checkItems;  // checkItems为assets中存储的2个检查项数组
  constructor() { }

  addToChecks(checkItem: CheckItem){
    this.items.push(checkItem);
  }
}

组件代码

import { Component, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';

import { CheckItem } from '../check-item';
import { ChecksService } from '../checks.service';
import { checkItems } from 'src/assets/journaldata';
import { CheckItemComponent } from '../check-item/check-item.component';

@Component({
  selector: 'app-new-check-item',
  templateUrl: './new-check-item.component.html',
  styleUrls: ['./new-check-item.component.css']
})
export class NewCheckItemComponent implements OnInit {

  private newCheckItem : CheckItem = {id: 5, name: "Why"};

  checkItemForm = this.formBuilder.group({
    checkName : ''
  })
  
  onSubmit(): void {
    console.log(this.newCheckItem.name);
    this.newCheckItem.name = this.checkItemForm.value.checkName!;
    this.checkItemForm.reset();
    console.log(this.newCheckItem.name);
    this.checkService.addToChecks(this.newCheckItem);
    this.checkService.addToChecks({id:5, name:this.newCheckItem.name})
    console.log(checkItems);
  }

  constructor(
    private checkService: ChecksService,
    private formBuilder : FormBuilder
    ) {}

  ngOnInit(): void {
  }

}

控制台输出

Why
TestName
(4) [{…}, {…}, {…}, {…}]
0: {id: 1, name: 'Test'}
1: {id: 2, name: 'Another Test'}
2: {id: 5, name: ''}
3: {id: 5, name: 'TestName'}

问题原因与解决方法

核心原因:对象引用共享 + 双向绑定异步更新

问题本质是对象引用复用和Angular表单的异步变更检测共同作用的结果:

  1. 你在组件中复用了固定的newCheckItem对象,调用addToChecks(this.newCheckItem)时,推入数组的是该对象的引用,而非独立的新对象。
  2. 你的模板大概率将newCheckItem.name与表单控件checkName做了双向绑定(比如[(ngModel)]="newCheckItem.name")。
  3. 执行this.checkItemForm.reset()时,表单控件值被同步清空,但Angular的变更检测是异步执行的:
    • 同步代码里的console.log(this.newCheckItem.name)还能拿到赋值后的TestName
    • 后续变更检测触发后,双向绑定会把表单的空值同步到newCheckItem.name,导致数组中共享引用的对象name被清空
  4. 第二个addToChecks传入的是新创建的独立对象,它保存了当时的TestName值,不受后续变更影响。

解决方法

任选以下一种方案即可:

  1. 每次提交创建新对象,避免复用
    直接基于表单值生成新对象,不再依赖newCheckItem:

    onSubmit(): void {
      const name = this.checkItemForm.value.checkName!;
      this.checkService.addToChecks({id: 5, name});
      this.checkItemForm.reset();
      console.log(checkItems);
    }
    
  2. 传递对象副本而非引用
    使用对象展开语法创建newCheckItem的副本,切断引用关联:

    this.checkService.addToChecks({...this.newCheckItem});
    
  3. 移除双向绑定,仅通过表单取值
    若模板中有双向绑定,改为单向绑定或直接通过表单控件获取值,切断表单与newCheckItem的自动同步:

    <!-- 原双向绑定示例 -->
    <!-- <input formControlName="checkName" [(ngModel)]="newCheckItem.name"> -->
    <!-- 修改为仅使用表单控件 -->
    <input formControlName="checkName">
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 03:36:21