@for 在removeAt(index) 上的表单表现得很奇怪

问题描述 投票:0回答:1

编辑:稍微改进了 stackblitz,以便人们可以并排比较它。

对于 Angular 17+,我想使用新的模板语法来迁移我的项目,这似乎有一个奇怪的行为,而且我还没有看到任何人谈论它。在旧的

*ngFor
循环中使用removeAt删除项目似乎工作得很好。然而,当我在
@for
中尝试相同的操作时......无论索引是否实际上准确(您可以控制台记录它,它确实每次都指向正确的控件),但使用removeAt它只是继续删除数组中的最后一项。我是不是理解错了
@for
?我在这篇文章的末尾添加了对我的问题的最小重现,作为堆栈闪电战

import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import {
  FormArray,
  ReactiveFormsModule,
  UntypedFormBuilder,
} from '@angular/forms';
import 'zone.js';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-root',
  imports: [ReactiveFormsModule, CommonModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
  standalone: true,
  template: `
    <form [formGroup]="baseSearchForm">

      <div>
        <input type="text" formControlName="search" placeholder="Search for ...">
      </div>

      <div style="margin-top: 1rem; background: lightgray; padding: 1rem; " formArrayName="searchParams">

       <!-- removeFilter here will always successfully delete the right control -->
        <!-- <div *ngFor="let control of searchParams.controls; let i = index;">
          <div style="display: flex; flex-direction: row;" [formGroupName]="i">
            <input type="text" formControlName="field" placeholder="... where column ...">
            <input type="text" formControlName="operator" placeholder="... is ...">
            <button *ngIf="i > 0" (click)="removeFilter(i)">Remove</button> 
          </div>
        </div> -->

        <!-- removeFilter will only delete the last element of the array, regardless of the index -->
        @for(control of searchParams.controls; track $index;) {
          <div style="display: flex; flex-direction: row;" [formGroupName]="$index">
            <input type="text" formControlName="field" placeholder="... where column ...">
            <input type="text" formControlName="operator" placeholder="... is ...">
            <button *ngIf="$index > 0" (click)="removeFilter($index)">Remove</button> 
          </div>
        }
      </div>
      <button style="margin-top: 1rem" (click)="addFilter()">Add Filter</button>
  </form>
  `,
})
export class App {
  fb = inject(UntypedFormBuilder);
  baseSearchForm = this.fb.group({
    search: [''],
    searchParams: this.fb.array([
      this.fb.group({
        field: [0],
        logicalOperator: [''],
        operator: [''],
        type: [''],
        values: [],
      }),
    ]),
  });

  get searchParams() {
    return this.baseSearchForm.get('searchParams') as FormArray;
  }

  removeFilter(index: number) {
    this.searchParams.removeAt(index);
  }

  addFilter() {
    this.searchParams.push(
      this.fb.group({
        field: [this.searchParams.length],
        logicalOperator: [''],
        operator: [''],
        type: [''],
        values: [],
      })
    );
  }
}

bootstrapApplication(App);

堆栈闪电战

angular angular-reactive-forms angular-forms
1个回答
0
投票

我将给出与 GitHub 问题相同的答案:

这是 dom 有状态的情况之一,您不应该使用索引作为跟踪键。

ngFor
指令默认使用indentity作为跟踪键,您可以通过跟踪
control
来执行相同的操作:

@for(control of searchParamsAlt.controls; track control)
© www.soinside.com 2019 - 2024. All rights reserved.