Angular 5 FormGroup reset doesn't reset validators

107,022

Solution 1

It (FormGroup) behaves correctly. Your form requires username and password, thus when you reset the form it should be invalid (i.e. form with no username/password is not valid).

If I understand correctly, your issue here is why the red errors are not there at the first time you load the page (where the form is ALSO invalid) but pop up when you click the button. This issue is particularly prominent when you're using Material.

AFAIK, <mat-error> check the validity of FormGroupDirective, not FormGroup, and resetting FormGroup does not reset FormGroupDirective. It's a bit inconvenient, but to clear <mat-error> you would need to reset FormGroupDirective as well.

To do that, in your template, define a variable as such:

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

And in your component class, call formDirective.resetForm():

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

GitHub issue: https://github.com/angular/material2/issues/4190

Solution 2

After reading the comments this is the correct approach

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

There was no need to call form.clearValidators()

Solution 3

In addition to Harry Ninh's solution, if you'd like to access the formDirective in your component without having to select a form button, then:

Template:

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

Component:

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

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

  constructor(... )

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

Solution 4

Add the property -

@ViewChild(FormGroupDirective) formGroupDirective: FormGroupDirective;

and use this instead of this.myForm.reset();

this.formGroupDirective.resetForm();

This will reset the error display and also do the job of form.reset(). But the form, along with the fields, will still show ng-invalid class

Check this answer for more details - https://stackoverflow.com/a/56518781/9262627

Solution 5

The below solution works for me when trying to reset specific form controller in form group -

 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();
Share:
107,022
efarley
Author by

efarley

Front End Web Engineer

Updated on January 22, 2022

Comments

  • efarley
    efarley over 2 years

    I have a form on my page and when I call FormGroup.reset() it sets the forms class to ng-pristine ng-untouched but FormControl.hasError(...) still returns truthy. What am I doing wrong here?

    Template

    <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>
    

    Component

    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/

  • schankam
    schankam over 5 years
    This issue really needs to be solved, like you said this is pretty inconvenient to have to use this workaround... Will do with it, good catch & thanks for the solution.
  • scourge192
    scourge192 over 5 years
    In my case, I had a very unique situation where I needed to reset the "submitted" without clearing the form values (which resetForm()) was doing to me. To get around this, I did (<any>formDirective).submitted = false; . Kind of a dirty hack but looking at the source code there's no obvious reason submitted needed to be readonly in their typescript definition.
  • Maximillion Bartango
    Maximillion Bartango over 5 years
    This will simply remove the validators, and not reset them.
  • Jorge Valvert
    Jorge Valvert over 5 years
    To clear the validators this.loginform.clearValidators() , then set the control's errors to null
  • ctilley79
    ctilley79 over 5 years
    Read the github issue where the official response was "not my department". Pretty lame, I expect better from google employees.
  • msanford
    msanford about 5 years
    With angular 7.2.2: Argument of type NgForm is not assignable to type FormGroupDirective
  • Sam
    Sam about 5 years
    I think this is a better answer. the one marked as the answer needs to pass a local local variable to the code behind, not desirable. But that answer has provided very good information around how form works.
  • Rajendra Thorat
    Rajendra Thorat over 4 years
    Found this answer after spending entire day. setTimeout() helped to resolve this. If you can add some description about how this 'hack' is working , will be helpful. Tested with angular 7.2.8
  • Jonathan Cardoz
    Jonathan Cardoz over 4 years
    For Angular 8, The `@ViewChild directive takes two params. The other parameter, apart from the string 'formDirective' is metadata properties. See angular.io/api/core/ViewChild#description for more info.
  • stayingcool
    stayingcool over 4 years
    Works with Angular 8.x as well!
  • Andreas
    Andreas almost 4 years
    While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value.
  • Petros Kyriakou
    Petros Kyriakou over 3 years
    works perfectly with Angular 9. Something else is wrong mbue
  • ukie
    ukie over 3 years
    great answer, but his not only resets validators, but also resets all values (entered by users) in the form.
  • laffuste
    laffuste over 3 years
    This is the only answer that worked for me in Angular 7 + material
  • dvdmn
    dvdmn about 3 years
    > It (FormGroup) behaves correctly. well it depends, I don't think it works correctly. form.reset() should "Reset" the whole form. Instead, it clears the values but leaves the errors. If we look at from 'what documentation says' point of view it works. but it does change the fact that whoever wrote the reset function was 'lazy' and this is just a half-baked function...
  • Silvos
    Silvos almost 3 years
    Still not fixed in Angular Material 12 (since about 4 years). So this workaround still needed. Shame on you Angular Material devs!
  • ganesh
    ganesh over 2 years
    first time its works fine. for second time it does not shows the errors as we set it to null. What can we do in that case?
  • Daniel Morris
    Daniel Morris about 2 years
    This worked perfectly for me. Thanks.