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

Angular FormArray调用removeAt删除测试用例仅首个按钮生效问题

问题原因

你代码的核心问题是两层索引混淆:

  • 第一层是questions FormArray的索引,代表第几个题目,你当前初始化的表单中只有1个题目,所以该层合法索引只有0
  • 第二层是每个题目下的testcases FormArray的索引,代表该题目下第几个测试用例
    你现在直接把测试用例的索引当成了questions数组的索引去取值,当测试用例数量超过1个时,取questions.controls[index]就会拿到undefined,访问undefined的controls属性就抛出了对应的报错。

修复方案

1. 修正HTML模板

补全外层questions数组的循环,明确区分题目索引qIndex和测试用例索引tIndex,删除/新增操作时传入正确的索引:

<form [formGroup]="parentForm">
  <!-- 外层遍历题目数组,拿到题目索引qIndex -->
  <div formArrayName="questions" *ngFor="let question of questionsArray.controls; index as qIndex">
    <div [formGroupName]="qIndex">
      <div formArrayName="testcases">
        <!-- 内层遍历当前题目下的测试用例,拿到测试用例索引tIndex -->
        <div
          class="form-control grid grid-cols-2 justify-between mb-8 w-xl"
          *ngFor="let testcase of getTestcaseControl(qIndex); index as tIndex"
          [formGroupName]="tIndex"
        >
          <div class="flex">
            <div class="form-control m-0">
              <label class="label">
                <span class="label-text">输入值</span>
              </label>
              <input
                type="text"
                placeholder="2 3 4"
                formControlName="input"
              />
              <label class="label">
                <p
                  *ngIf="checkFormInput(testcase, 'input')"
                  class="invalid-feedback"
                >
                  请输入该测试用例的输入值
                </p>
              </label>
            </div>

            <div class="form-control m-0">
              <label class="label">
                <span class="label-text">输出值</span>
              </label>
              <div class="flex items-center">
                <input
                  type="text"
                  placeholder="12 34 84"
                  formControlName="output"
                />
                <!-- 删除时传入两个索引参数 -->
                <img
                  src="../../../../assets/images/course/course-leaderboard-avatars/cancel.png"
                  class="w-6 h-6 cursor-pointer py-auto mt-2 mx-2"
                  alt="删除图标"
                  (click)="onDeleteTestcase(qIndex, tIndex)"
                />
              </div>

              <label class="label">
                <p
                  *ngIf="checkFormInput(testcase, 'output')"
                  class="invalid-feedback"
                >
                  请输入该测试用例的输出值
                </p>
              </label>
            </div>
          </div>
          <div class="form-control m-0 w-6"></div>
        </div>
      </div>
      <!-- 新增测试用例时传入当前题目索引 -->
      <div class="flex justify-end mr-4">
        <button class="btn btn-secondary" type="button" (click)="onAddTestcase(qIndex)">
          新增测试用例
        </button>
      </div>
    </div>
  </div>
</form>

注意:新增按钮需要加type="button",否则会触发表单默认提交行为。

2. 优化组件逻辑,简化类型转换

先给组件添加两个通用getter,避免重复的类型转换代码:

// 获取题目数组
get questionsArray(): FormArray {
  return this.parentForm.get('questions') as FormArray;
}

// 根据题目索引获取对应测试用例数组
getTestcasesArray(qIndex: number): FormArray {
  return this.questionsArray.at(qIndex).get('testcases') as FormArray;
}

3. 修正原有方法

修正获取测试用例列表的方法

getTestcaseControl(qIndex: number) {
  return this.getTestcasesArray(qIndex).controls;
}

修正删除测试用例方法

onDeleteTestcase(qIndex: number, tIndex: number) {
  this.getTestcasesArray(qIndex).removeAt(tIndex);
}

简化新增测试用例方法

onAddTestcase(qIndex: number) {
  this.getTestcasesArray(qIndex).push(
    this.fb.group({
      input: ['', [Validators.required('请输入该测试用例的输入值')]],
      output: ['', [Validators.required('请输入该测试用例的输出值')]],
    })
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 11:09:02