Angular 5 FormGroup 重置不会重置验证器

Angular 5 FormGroup reset doesn't reset validators

我的页面上有一个表单,当我调用 FormGroup.reset() 时,它将表单 class 设置为 ng-pristine ng-untouchedFormControl.hasError(...) 仍然 returns 真实。我在这里做错了什么?

模板

<form [formGroup]="myForm" (ngSubmit)="submitForm(myForm)">
  <mat-form-field>
    <input matInput formControlName="email" />
    <mat-error *ngIf="email.hasError('required')">
      Email is a required feild
    </mat-error>
  </mat-form-field>
  <mat-form-field>
    <input matInput type="password" formControlName="password" />
    <mat-error *ngIf="password.hasError('required')">
      Password is a required feild
    </mat-error>
  </mat-form-field>
  <button type="submit">Login</button>
</form>

组件

export class MyComponent {
  private myForm: FormGroup;
  private email: FormControl = new FormContorl('', Validators.required);
  private password: FormControl = new FormControl('', Validators.required);

  constructor(
    private formBuilder: FormBuilder
  ) {
    this.myForm = formBuilder.group({
      email: this.email,
      password: this.password
    });
  }

  private submitForm(formData: any): void {
    this.myForm.reset();
  }
}

笨蛋

https://embed.plnkr.co/Hlivn4/

它 (FormGroup) 行为正确。您的表单需要用户名和密码,因此当您重置表单时它应该是无效的(即没有 username/password 的表单无效)。

如果我没理解错的话,你的问题是为什么在你第一次加载页面时红色错误不存在(表单也无效)但是当你点击按钮时弹出。当您使用 Material.

时,此问题尤为突出

AFAIK,<mat-error> 检查 FormGroupDirective 的有效性,而不是 FormGroup,并且重置 FormGroup 不会重置 FormGroupDirective。这有点不方便,但要清除 <mat-error>,您还需要重置 FormGroupDirective

为此,在您的模板中定义一个变量:

<form [formGroup]="myForm" #formDirective="ngForm" 
  (ngSubmit)="submitForm(myForm, formDirective)">

然后在您的组件 class 中调用 formDirective.resetForm():

private submitForm(formData: any, formDirective: FormGroupDirective): void {
    formDirective.resetForm();
    this.myForm.reset();
}

GitHub 问题:https://github.com/angular/material2/issues/4190

阅读评论后这是正确的方法

// you can put this method in a module and reuse it as needed
resetForm(form: FormGroup) {

    form.reset();

    Object.keys(form.controls).forEach(key => {
      form.get(key).setErrors(null) ;
    });
}

不需要调用 form.clearValidators()

除了 Harry Ninh 的解决方案之外,如果您想访问组件中的 formDirective 而无需 select 表单按钮,则:

模板:

<form 
  ...
  #formDirective="ngForm" 
>

组件:

import { ViewChild, ... } from '@angular/core';
import { NgForm, ... } from '@angular/forms';

export class MyComponent {
 ...
 @ViewChild('formDirective') private formDirective: NgForm;

  constructor(... )

  private someFunction(): void { 
    ...
    formDirective.resetForm();
  }
}

我也遇到了同样的问题。我的问题是我使用的是 mat-form-fieldformGroup。重置表单后 submitted 标志未重置。

所以,对我有用的解决方案是,将 ngForm 指令与 formGroup 一起放置并传递 onSubmit(form)。添加 @ViewChild('form') form; 在组件中,然后我使用 this.form.resetForm();

我发现在调用resetForm()和reset()后,submitted并没有被reset,仍然是true,导致显示错误信息。这个解决方案对我有用。我在寻找在输入标签上调用 select() 和 focus() 的解决方案时发现了它,但它也没有按预期工作。只需将您的行包装在 setTimeout() 中。我认为 setTimeout 强制 Angular 检测变化,但我可能是错的。这有点骇人听闻,但确实有效。

<form [formGroup]="myFormGroup" #myForm="ngForm">
    …
    <button mat-raised-button (click)="submitForm()">
</form>
submitForm() { 
    …
    setTimeout(() => {
        this.myForm.resetForm();
        this.myFormGroup.reset();
    }, 0);
}

当我尝试重置表单组中的特定表单控制器时,以下解决方案对我有用 -

 this.myForm.get('formCtrlName').reset();
 this.myForm.get('formCtrlName').setValidators([Validators.required, Validators.maxLength(45), Validators.minLength(4), Validators.pattern(environment.USER_NAME_REGEX)]);
 this.myForm.get('formCtrlName').updateValueAndValidity();

以上对我没有任何帮助(Angular 7.2,Angular Material 7.3.7)。

尝试使用提交方法传递视图中的事件:

<form [formGroup]="group" (ngSubmit)="onSubmit($event)">
    <!-- your form here -->
</form>

然后用它来重置 currentTarget 和你的表单:

public onSubmit(event): void {
  // your code here
  event.currentTarget.reset()
  this.group.reset()
}

简单修复:将按钮与 type="reset" 和函数 submitForm() 一起使用

<form [formGroup]="MyForm" (ngSubmit)="submitForm()">
  <input formControlName="Name">
  <mat-error>  
    <span *ngIf="!tunersForm.get('Name').value && tunersForm.get('Name').touched"></span>  
  </mat-error>
  <button type="reset" [disabled]="!MyForm.valid" (click)="submitForm()">Save</button>
</form>

我没能重置表单指令。但是您也可以将输入状态更改为待处理。

this.myForm.get("email").reset();
this.myForm.get("password").reset();

对于这可能有帮助的任何人,我是 运行 Angular 9.1.9 我不想重置 form/controls 只是表格的整体有效性所以我只是 运行:

this.registerForm.setErrors(null);

...其中 registerForm: FormGroup 并重置表单错误,导致:

this.registerForm.valid

...返回 true

控件也可以这样做:

this.registerForm.get('email').setErrors(null)

一旦表单被触摸,这些错误就会被重新评估,所以如果这还不够好,您可能需要一个布尔标志来进一步确定您想要开始的时间 showing/hiding 错误 UI.

我不需要触及指令。

 resetForm() {
    this.myFormGroup.reset();
    this.myFormGroup.controls.food.setErrors(null);
    this.myFormGroup.updateValueAndValidity();
  } 

添加 属性 -

@ViewChild(FormGroupDirective) formGroupDirective: FormGroupDirective;

并使用它代替 this.myForm.reset();

this.formGroupDirective.resetForm();

这将重置错误显示并完成 form.reset() 的工作。但是表单和字段仍然会显示 ng-invalid class

查看此答案了解更多详情 -

form.reset() 无法在像 Angular Material 这样的自定义表单控件上工作,这就是该功能未按预期工作的原因。

我的解决方法是这样的

    this.form.reset();
    for (let control in this.form.controls) {
      this.form.controls[control].setErrors(null);
    }

this.form.reset() 问题在于它会重置您的表单控件值而不是错误,因此您需要通过这行代码单独重置它们

for (let control in this.form.controls) {
      this.form.controls[control].setErrors(null);
    }

有了这个你就不需要使用 FormGroupDirective 这对我来说是一个更干净的解决方案。

Github 问题:https://github.com/angular/angular/issues/15741

2021 年更新 - ANGULAR 11.2

直接在 HTML 函数中使用 [formGroup]="form#formDirective="ngForm" 并不是一个好习惯。或者您可能更愿意使用 @ViewChild,并直接从您的 .ts 中执行。其实,问题不在于Angular,而在于Material.

如果你看看他们的 GitHub,你会看到这个:

/** Provider that defines how form controls behave with regards to displaying error messages. */
@Injectable({providedIn: 'root'})
export class ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    return !!(control && control.invalid && (control.touched || (form && form.submitted)));
  }
}

表单将保持其 submitted 状态。所以你只需要删除函数的最后一部分。 这是我的解决方案(已测试并有效)。我有一个 Material 模块,我已经实现了这个:

export class ShowOnInvalidTouchedErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl): boolean {
    return !!(control && control.invalid && control.touched);
  }
}

@NgModule({
  providers: [
    {
      provide: ErrorStateMatcher, useClass: ShowOnInvalidTouchedErrorStateMatcher
    }
  ],
  exports: [
    MatSnackBarModule,
    MatTabsModule,
    ...
  ]
});

如果您只想在一种形式上使用此 ErrorStateMatcher,可以。请参阅 this Material example。这也是一样的道理。