【问题标题】:Why is mat-form-field not editable inside this FormArray?为什么这个 FormArray 中的 mat-form-field 不可编辑?
【发布时间】:2019-09-28 15:26:43
【问题描述】:

除了我不能编辑其中的任何mat-form-fields 之外,以下表单几乎完成了它应该做的事情。它们没有被禁用,只是无法编辑它们。

这是什么原因?

见:StackBlitz

我看不出与 StackBlitz 上的 this example 有什么不同。

import {Component} from '@angular/core';
import {FormBuilder, FormGroup, FormArray, FormControl} from '@angular/forms';

@Component({
  selector: 'form-field-label-example',
  templateUrl: 'form-field-label-example.html',
  styleUrls: ['form-field-label-example.css'],
})
export class FormFieldLabelExample {

  public dataList = [
    {name: 'Alice'},
    {name: 'Bob'}
  ]

  public form: FormGroup;

  constructor(private _fb: FormBuilder) {

    const formArray = this._fb.array([]);  

    for (const data of this.dataList) {
      formArray.push(
        this._fb.group({name: new FormControl(data.name)})
      );      
    }

    this.form = this._fb.group({
      offers: formArray
    });

  }

}
<div>

  <form [formGroup]="form">

    <div formArrayName="offers" *ngFor="let data of form.controls.offers?.value; let i = index;">
      <ng-container [formGroupName]="i">
        <mat-form-field>
          <input matInput placeholder="Name" formControlName="name">
        </mat-form-field>
      </ng-container>
    </div>

  </form>

  <span *ngIf="form.dirty">DIRTY</span>

  <mat-form-field>
    <input matInput placeholder="Name">
  </mat-form-field>

</div>

【问题讨论】:

  • 奇怪...好像它们不是双向绑定。您可以取出 mat-form-field 和 matInput,这至少可以让您在表单上设置脏标志。这样做会给它带来与其他 stackblitz 相同的奇怪行为,它似乎一次只想要一个角色。
  • @pmiller3 我刚刚将我在问题中引用的第二个示例更改为使用matInputmat-form-field (example) - 它也在那里不起作用。我猜这是一个错误?

标签: angular typescript angular-material


【解决方案1】:

确实和ngFor的渲染行为有关。

更准确地说,如果 ngFor 中使用的数组更改了对该数组元素的引用(并且我们没有提供自定义 trackBy 函数来覆盖比较),那么 Angular 将识别这些更改并重新渲染视图。

Angular 表单通常可以触发所有控件层次结构的AbstractControl.updateValueAndValidity 方法。

FormControl -> parent -> FormGroup -> parent -> FormArray -> parent -> FormGroup

对于FormGroup,它会触发以下方法:

_updateValue(): void { (this as{value: any}).value = this._reduceValue(); }

这基本上是创建一个新对象,即{name: 'Alice'},下次触发它时,它将用一个新对象覆盖当前值。

{name: 'Alice'} !== {name: 'Alice'}

这正是 Angular 默认在 ngForOf 指令中识别的差异。

你可能会问?

那为什么我在 StackBlitz 上看不到与 this example 的区别?

在该示例中,问题仍然存在,但乍一看无法看出。

|username  password
  /\
click here(it works)

username  password
            /\
     then click here(it doesn't since the dom was rerendered)

你的例子和上面例子的区别在于你使用了材质控制,AbstractControl.updateValueAndValidity的触发发生得更早。

那么解决办法是什么?

1) 前面提到的trackBy属性

trackByFn(index) {
  return index;
}

*ngFor="let data of form.controls.offers?.value; let i = index; trackBy: trackByFn"

Stackblitz Example

2) 将this.form.controls.offers.value 分配给组件属性并使用它。

arr;
...
this.arr = this.form.controls.offers.value;

*ngFor="let data of arr; let i = index;"

Stackblitz Example

【讨论】:

  • 这个答案值得奖励。不仅输入有效,dirty 标志也被正确设置。我想多亏了这一点,我就不必乱用@ViewChildren 和丑陋的更新逻辑!谢谢大佬!
  • 原来使用offers.value时会出现问题——切换到offers.controls也可以解决问题。为记录添加Stackblitz Example(来源github)。
  • 那是因为updateValueAndValidity调用后控件保持不变
  • 需要注意的一点,trackBy 的语法是trackByFn(index: any, item: any)。这使您可以通过项目的唯一 ID 进行跟踪,而不仅仅是使用索引。
【解决方案2】:

我刚刚更改了您的代码,现在它可以工作了。 请检查一下。如果这不是您真正想要的,也许您可​​以找到一些想法来解决您的问题。

https://stackblitz.com/edit/angular-ckpey4-bxiidz?file=app/form-field-label-example.html

【讨论】:

  • 可以编辑输入,但 FormArray 不再被标记为脏。这就是我想要使用它的原因。
猜你喜欢
  • 2019-11-12
  • 1970-01-01
  • 2018-08-24
  • 2020-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-27
  • 2018-04-16
相关资源
最近更新 更多