【问题标题】:Angular 5 FormGroup reset doesn't reset validatorsAngular 5 FormGroup 重置不会重置验证器
【发布时间】:2018-06-21 07:54:51
【问题描述】:

我的页面上有一个表单,当我调用FormGroup.reset() 时,它会将表单类设置为ng-pristine ng-untouched,但FormControl.hasError(...) 仍然返回truthy。我在这里做错了什么?

模板

<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();
  }
}

Plunker

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

【问题讨论】:

标签: html angular typescript angular-material angular-reactive-forms


【解决方案1】:

2021 年更新 - 角度 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 状态。所以你只需要删除函数的最后一部分。 这是我的解决方案(经过测试且有效)。我有一个材料模块,我已经实现了这个:

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。原理是一样的。

【讨论】:

    【解决方案2】:

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

    我的解决方法是这样的

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

    this.form.reset() 这个问题是它会重置你的 formcontrol 值而不是错误,所以你需要通过这行代码单独重置它们

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

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

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

    【讨论】:

      【解决方案3】:

      添加属性 -

      @ViewChild(FormGroupDirective) formGroupDirective: FormGroupDirective;
      

      并用这个代替this.myForm.reset();

      this.formGroupDirective.resetForm();
      

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

      查看此答案了解更多详情 - https://stackoverflow.com/a/56518781/9262627

      【讨论】:

      • 这对我来说非常有效。谢谢。
      【解决方案4】:
       resetForm() {
          this.myFormGroup.reset();
          this.myFormGroup.controls.food.setErrors(null);
          this.myFormGroup.updateValueAndValidity();
        } 
      

      【讨论】:

      • 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。
      【解决方案5】:

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

      this.registerForm.setErrors(null);
      

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

      this.registerForm.valid
      

      ...返回true

      对控件也可以这样做:

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

      一旦触摸表单,无论如何都会重新评估这些错误,因此如果这还不够好,您可能需要一个布尔标志来进一步确定何时要开始显示/隐藏错误 UI。

      在我的情况下,我不需要触摸指令。

      【讨论】:

        【解决方案6】:

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

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

        【讨论】:

          【解决方案7】:

          简单修复:将按钮与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>
          

          【讨论】:

            【解决方案8】:

            上面没有对我有用(Angular 7.2,Angular Material 7.3.7)。

            尝试使用 submit 方法传递视图上的事件:

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

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

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

            【讨论】:

              【解决方案9】:

              我发现调用resetForm() 和reset() 后,提交并没有被重置并保持为真,导致显示错误消息。这个解决方案对我有用。我在寻找在输入标签上调用 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);
              }
              

              【讨论】:

              • 花了一整天的时间找到了这个答案。 setTimeout() 帮助解决了这个问题。如果您可以添加一些有关此“黑客”如何工作的描述,将会很有帮助。使用角度 7.2.8 测试
              【解决方案10】:

              以下解决方案在尝试重置表单组中的特定表单控制器时适用于我 -

               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();
              

              【讨论】:

                【解决方案11】:

                阅读完 cmets 后,这是正确的做法

                // 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()

                【讨论】:

                • 这只会删除验证器,而不是重置它们。
                • 清除验证器 this.loginform.clearValidators() ,然后将控件的错误设置为 null
                • 与 Angular 9 完美配合。mbue 还有其他问题
                • 第一次运行良好。第二次它没有显示错误,因为我们将其设置为空。在这种情况下我们能做什么?
                【解决方案12】:

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

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

                【讨论】:

                  【解决方案13】:

                  除了 Harry Ninh 的解决方案之外,如果您想在无需选择表单按钮的情况下访问组件中的 formDirective,那么:

                  模板:

                  <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();
                    }
                  }
                  

                  【讨论】:

                  • 我认为这是一个更好的答案。标记为答案的那个需要将一个局部变量传递给后面的代码,这是不可取的。但该答案提供了有关表单如何工作的非常好的信息。
                  • 对于 Angular 8,`@ViewChild 指令需要两个参数。除了字符串“formDirective”之外,另一个参数是元数据属性。请参阅angular.io/api/core/ViewChild#description 了解更多信息。
                  • 很好的答案,但他不仅会重置验证器,还会重置表单中的所有值(由用户输入)。
                  • 这是在 Angular 7 + 材料中对我有用的唯一答案
                  【解决方案14】:

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

                  如果我理解正确,您的问题是为什么在您第一次加载页面时没有出现红色错误(表单也无效),但是当您单击按钮时会弹出。当您使用 Material 时,这个问题尤为突出。

                  AFAIK,&lt;mat-error&gt; 检查FormGroupDirective 的有效性,而不是FormGroup,重置FormGroup 不会重置FormGroupDirective。这有点不方便,但要清除&lt;mat-error&gt;,您还需要重置FormGroupDirective

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

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

                  在你的组件类中,调用formDirective.resetForm():

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

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

                  【讨论】:

                  • 这个问题真的需要解决,就像你说的那样,不得不使用这个解决方法很不方便......会用它,很好的解决方案,感谢您的解决方案。
                  • 就我而言,我遇到了一个非常独特的情况,我需要在不清除表单值(resetForm() 对我所做的)的情况下重置“提交”。为了解决这个问题,我做了(&lt;any&gt;formDirective).submitted = false;。有点肮脏的黑客,但查看源代码没有明显的理由提交需要在他们的打字稿定义中是只读的。
                  • 阅读官方回复“不是我的部门”的 github 问题。很蹩脚,我希望谷歌员工做得更好。
                  • 带角度 7.2.2:Argument of type NgForm is not assignable to type FormGroupDirective
                  • Angular Material 12 中仍未修复(大约 4 年以来)。所以仍然需要这种解决方法。为你的 Angular Material 开发者感到羞耻!
                  猜你喜欢
                  • 1970-01-01
                  • 2018-07-10
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-07-30
                  • 1970-01-01
                  • 2018-09-11
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多