dhilt
dhilt

Reputation: 20734

Angular NgForm: reset exact form filed value does not make it valid

I have a form on a component's template:

<form (ngSubmit)="submitLogin(loginForm)" #loginForm="ngForm">
  <mat-input-container>
    <input matInput [placeholder]="'User Name'" ngModel name="username" autofocus required>
  </mat-input-container>
  <br>
  <mat-input-container>
    <input matInput [placeholder]="'Password'" ngModel type="password" name="password" required>
  </mat-input-container>
  <br>
  <button [disabled]="loginForm.invalid" mat-raised-button type="submit">
    Login
  </button>
</form>

And here is my component's submit handler:

public submitLogin(loginForm: NgForm) {
  return this.authService.login(loginForm.value)
    .subscribe(
      res => this.router.navigate(['/customers']),
      error => loginForm.controls.password.reset() // this place!
    );
}

It works and on login error (passing some random values) I see

enter image description here

Question. How can I reset exact fileld on the Form and make it truly pristine and untouched? So it should be valid without marking it with red "invalid" border on the UI.

Right after loginForm.controls.password.reset() I see that loginForm.controls.password.touched is false and loginForm.controls.password.pristine is true, but I see also that loginForm.controls.password.status is "INVALID". If I hack it and directly assign the "VALID" value to the status property, the red invalid border disappeares, but it breaks lost-focus invalidation in case I focus on that filed and then go away without any input. There should be a legal way to reset form filed and make it valid in the same time.

Upvotes: 4

Views: 2444

Answers (2)

AVJT82
AVJT82

Reputation: 73337

This seems to be a known issue. According to this the error state is calculated like:

isInvalid && (isTouched || isSubmitted)

So when you have submitted your form, the isSubmitted flag is set to true, thus the condition is fulfilled and your field is shown as red. There are some workarounds, if you were to reset the whole form, you could use resetForm instead, but here you only want to reset one field, so...

There is an suggestion to use ErrorStateMatcher:

<input matInput 
   [placeholder]="'Password'" 
   ngModel type="password" 
   name="password" required 
   [errorStateMatcher]="matcher">

ErrorStateMatcher:

export class MyErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    // show errors when touched and invalid
    return (control.invalid && control.touched);
  }
}

and declare a ErrorStateMatcher in your TS:

matcher = new MyErrorStateMatcher();

Seems to work: StackBlitz

Upvotes: 4

Hugo Noro
Hugo Noro

Reputation: 3243

You can make use of markAs existing functions

Something like this

this.loginForm.controls.password.reset()
this.loginForm.controls.password.markAsPristine()
this.loginForm.controls.password.markAsUntouched()
this.loginForm.controls.password.updateValueAndValidity()

These are actual API functions for when you want to enforce a particular state and don’t want to depend entirely on Angular decision of what should the be the state of the field

Check here how to better use the updateValueAndValidity method

Upvotes: 0

Related Questions