【问题标题】:Reset Angular Reactive FormArray Values重置 Angular Reactive FormArray 值
【发布时间】:2019-01-31 03:50:15
【问题描述】:

我在 Angular 响应式表单中遇到了一个奇怪的错误。我正在使用 FormArray 创建一个可以添加或删除字段的表单。我还希望能够重置表单,使表单输入的值和数量恢复到原始数量。我目前能够实例化表单,添加字段并删除它们,但是当我按下重置时,我创建的函数首先清空 FormArray,然后使用与最初设置表单相同的过程重新创建字段, 值不能正确显示。不知道为什么会这样,可能是和 html 中用来绑定表单的 formControlNames 有关?

有谁知道导致问题的原因或重置表单值的正确方法是什么?

我在这里创建了一个堆栈闪电战:https://stackblitz.com/edit/angular-reactive-formarray-bug

这是我的组件代码。

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

import { ContentTemplate, ContentTemplateEntry, NewContentEntry } from './models';

import {Observable, Subscription, of} from 'rxjs';

import data from './template-data.json';

@Component({
  selector: 'material-app',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.scss']
})
export class AppComponent {
  public templateForm: FormGroup;

  public contentTemplate$: Observable<ContentTemplate>;
  public activeTemplate: ContentTemplate;
  public templateSub: Subscription;
  public entries: ContentTemplateEntry[];

  get templateEntries(): FormArray {
    return <FormArray>this.templateForm.get('entries');
  }

  constructor(
    private fb: FormBuilder
  ) {
    this.contentTemplate$ = of(data)
  }

  ngOnInit(): void {
    this.templateSub = this.contentTemplate$.subscribe((template: ContentTemplate) => {
      this.activeTemplate = {...template};
      this.entries = [...template.entries];
      this.templateForm = this.fb.group({
        entries: this.fb.array([])
      });
      this._processEntries(this.entries);
    });
  }

  ngOnDestroy(): void {
    this.templateSub.unsubscribe();
  }

  private _buildEntry(entry: ContentTemplateEntry) {
    const g = this.fb.group({
      id: {value: entry.id},
      title: {value: entry.title, disabled: entry.isRequired},
      orderNumber: {value: entry.orderNumber},
      type: {value: entry.type}
    });
    return g;
  }

  private _processEntries(entries: ContentTemplateEntry[])  {
    entries.forEach((e, i) => {
      this.templateEntries.push(this._buildEntry(e));
    });
  }

  private _getOrderNumber(): number {
    return this.templateEntries.length + 1;
  }

  private _removeItemsFromEntries(): void {
    while (this.templateEntries.length > 0) {
      this.templateEntries.removeAt(0);
    }
  }

  // reinstantiate using the same approach as before
  public resetForm() {
    this._removeItemsFromEntries();
    this._processEntries(this.entries);
  }

  public removeEntry(id: number) {
    this.templateEntries.removeAt(id);
  }

  public addEntry() {
    this.templateEntries.push(
      this._buildEntry(new NewContentEntry({
        orderNumber: this._getOrderNumber()
      }))
    );
  }

  public save() {
    console.log('save triggered');
  }
}

【问题讨论】:

    标签: angular angular6


    【解决方案1】:

    您的错误是由重置按钮上的type="reset" 引起的。删除此属性或替换为type="button" 将解决问题。

    重置表单的正确方法是在您的templateForm 属性上调用reset() 方法。

    重置 FormGroup,将所有后代标记为原始和未触及,并将所有后代的值设为 null。

    Official FormGroup documentation

    【讨论】:

    • 谢谢!这似乎是解决方案!这解决了我的问题,意味着我不需要将 _processEntries 函数包装在 setTimeout 中。再次感谢!
    【解决方案2】:

    我认为这个问题与反应形式的异步操作有关。 setTimeout 解决了这个问题,但我认为不是最好的方法。

    public resetForm() {
      setTimeout(() => this._processEntries(this.entries));
    }
    

    您还可以重构您的 processEntries 方法。

    private _processEntries(entries: ContentTemplateEntry[]) {
      const resEntries = entries.map(e => this._buildEntry(e));
      this.templateForm.setControl('entries', this.fb.array(resEntries));
    }
    

    而且 _removeItemsFromEntries 不需要更多。

    这里的例子https://stackblitz.com/edit/angular-reactive-formarray-bug-pvj4cm

    【讨论】:

    • 感谢您的帮助 Kliment。在我只是调度一个动作以在商店中重新创建原始对象然后过滤到组件之前,我有一个解决方法。这绝对比那更干净。
    • 你好 Kliment,@Andreas 发现我的表单有问题。错误地应用于我的重置按钮的重置类型属性意味着表单在我重置之前已被更改。我猜 setTimeout 解决方案必须通过将 processEntries 函数推到事件循环的后面来起作用。
    【解决方案3】:

    我使用过一次 FormArray,我做了以下操作:

    const control = <FormArray>this.templateForm.controls['entries'];
    for(let i = control.length-1; i >= 0; i--) {
      control.removeAt(i)
    }
    

    【讨论】:

      【解决方案4】:

      这样更好:

      const control = <FormArray>this.templateForm.controls['entries'];
      while (control.length > 0) {
        control.removeAt(0)
      }
      

      【讨论】:

      • 欢迎来到 StackOverflow!请提供带有参考资料的具体答案。如果您有任何建议或需要澄清,请发表评论。我们有关于How to write a good answer 的指导方针。
      猜你喜欢
      • 2020-07-24
      • 2018-12-22
      • 2019-06-22
      • 2018-12-22
      • 2020-12-19
      • 1970-01-01
      • 2022-12-27
      • 2021-05-30
      • 2023-03-07
      相关资源
      最近更新 更多