使用 FormArray 动态控制 FormGroups 的数量

Dynamically control the number of FormGroups with a FormArray

我正在尝试使用 FormArray 动态控制 FormGroup 的数量,以便设置 FormArray 长度。

myForm = this.fb.group({
  formGroupsToAdd: ['5'],
  formList: this.fb.array(this.getFormControls(5))
});

getFormControls(numberOfFormGroups: number) {
  const myArray = new Array(numberOfFormGroups);
  myArray.fill(this.fb.group({
    length: [''],
    width: [''],
    height: ['']
  }));
  return myArray;
}

我也有办法用

得到这个formList: FormArray
get unitList() {
  return this.myForm.get('formList') as FormArray;
}

但是,当我向 FormArray 中的单个控件添加值时,它会更新 FormArray 中的所有 FormGroup

<div formArrayName="formList">
  <div *ngFor="let unitForm of formList.controls; let i = index">
    <div [formGroup]="formList.controls[i]">
      <pre>{{formList.controls[i].value | json}}</pre>
      <pre>i = {{i}}</pre>
      <mat-form-field>
        <input matInput formControlName="length" />
      </mat-form-field>
      <mat-form-field>
        <input matInput formControlName="width" />
      </mat-form-field>
      <mat-form-field>
        <input matInput formControlName="height" />
      </mat-form-field>
    </div>
  </div>
</div>

所以我的 formList.value 看起来像这样...

[
  {
    "length": "",
    "width": "asdf",
    "height": ""
  },
  {
    "length": "",
    "width": "asdf",
    "height": ""
  },
  {
    "length": "",
    "width": "asdf",
    "height": ""
  },
  {
    "length": "",
    "width": "asdf",
    "height": ""
  },
  {
    "length": "",
    "width": "asdf",
    "height": ""
  }
]

我需要一个 FormGroup 来包含它自己独特的 FormControl 值。

我做错了什么?为什么 [formGroup]="formList.controls[i]" 不能通过为每个 formGroup 提供唯一标识符来满足我的需求?

编辑:我已经为我的问题添加了一个 stackblitz。
https://stackblitz.com/edit/angular-ivy-sa7pbb?file=src/app/app.component.html

您应该使用 [formGroupName]="i" 而不是 [formGroup]="formList.controls[i]"

<div formArrayName="formList">
  <div *ngFor="let unitForm of formList.controls; let i = index">
    <div [formGroupName]="i">
      <pre>{{formList.controls[i].value | json}}</pre>
      <pre>i = {{i}}</pre>
      <mat-form-field>
        <input matInput formControlName="length" />
      </mat-form-field>
      <mat-form-field>
        <input matInput formControlName="width" />
      </mat-form-field>
      <mat-form-field>
        <input matInput formControlName="height" />
      </mat-form-field>
    </div>
  </div>
</div>

你应该像这样使用 [formGroupName]="idx​​"。

<div formArrayName="formList">
  <div *ngFor="let unitForm of formList.controls; let i = index" [formGroupName]="i">
      <mat-form-field>
        <input matInput formControlName="length" />
      </mat-form-field>
      <mat-form-field>
        <input matInput formControlName="width" />
      </mat-form-field>
      <mat-form-field>
        <input matInput formControlName="height" />
      </mat-form-field>
  </div>
</div>

以及一些有用的功能:

// Call init form on init
ngOnInit(): void {
    this.initForm();
}

// init form here
initForm() {
    this.myForm = this.fb.group({
      formList: this.fb.array([])
    });

    // Call this 5 times or any times you want to init rows.
    this.addItem();
}

// Add new item to make an addition button on UI
addItem() {
    // You can enter input into newItem function to load data from API to make update/details screen.
    this.unitList.push(this.newItem(0, 0, 0));
}

// Your function to get fromList
unitList() {
    return this.myForm.get('formList') as FormArray;
}

// new Item as a FormGroup to add into main form and it will auto render on UI
newItem(length: number, width: number, height: number): FormGroup {
    const newGroup = this.fb.group({
      length: [length],
      width: [width],
      height: [height]
    })
    return newGroup;
}

// Clear all items to make a refresh/clean UI
clearAllList() {
    this.unitList.clear();
}

// Remove item at index to make a delete button
removeItem(index: number) {
    this.unitList.removeAt(idx);
}

已更新: 修复 stackblitz 上的问题:https://stackblitz.com/edit/angular-ivy-rtmpwc?file=src/app/app.component.ts

问题是 myArray.fill,当您使用它时,它会使用 1 个参考变量来设置数组中的所有 5 个项目。因此,当您更改一项时,它将引用为 5 项

getFormControls(numberOfFormGroups) {
    const myArray = new Array(numberOfFormGroups);
    myArray.fill(this.fb.group({
      length: [''],
      width: [''],
      height: ['']
    }));
    return myArray;
  }

所以固定函数应该是这样的。它将为 5 个项目创建 5 个引用。

getFormControls(numberOfFormGroups) {
    const myArray = new Array();
    for (let i = 1; i <= numberOfFormGroups; i++) {
      let newItem = this.fb.group({ length: [''], width: [''], height: [''] });
      myArray.push(newItem);
    }
    return myArray;
  }