Components: MatInput does not clear error state on form reset

Created on 21 Sep 2018  路  10Comments  路  Source: angular/components

This is my submiting function

public submitCategoryForm(): void {
this.buttonText = 'Processing...';
this.isClicked = true;

const categoryData = this.categoryForm.value;
this.categoryModel = new CategoryInputModel(
  categoryData['title'],
  categoryData['imageUrl']
);

this.categoryServices.addNewCategory(this.categoryModel).subscribe(() => {
  this.buttonText = 'Add';
  this.isClicked = false;
  this.categoryForm.reset({ title: '', imageUrl: '' });
});

}

What is the use-case or motivation for changing an existing behavior?

After submiting my form (successfully) and reset values of the form, validations for required value pop up. How i can submit my form clear values and to make sure validations are not shown.

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

Angular 6, Material 6.4.7

Is there anything else we should know?

P3 materiainput help wanted

Most helpful comment

I've been struggling with this issue the last couple days. The struggle being figuring out exactly what is doing what, and having to delve into way more of the sourcecode than I'd like.

As @mattrenaud said it's ultimately the ErrorStateMatcher that decides whether or not to display a mat-error / mat-form-field control. It's also important to realize that ErrorStateMatcher is part of the angular/material project, and nothing to do with regular angular forms. So studying only the angular forms documentation isn't going to get you a solution if you're using mat-error or material controls.

Another source of confusion for me was that it's the FormGroup directive that tracks submitted and not the FormGroup object that contains the data model. In fact the FormGroup object (where you define controls) doesn't even have the concept of 'submitted'.

Looking at source code reveals that submitted=true is only set in the following scenarios:

  • You have the [FormGroup] defined on an actual <form> control. If you have it on a <div> it will never trigger submittted to be true.
  • You manually call onSubmit(undefined) on the directive.
  • (it is readonly so you can't just set or unset it)

This all caused me a lot of confusion - and I feel like there's a slight disconnect between the angular core and angular material teams in providing a completely integrated model. By this I'm mostly concerned that the FormGroup data model has no concept of submitted, but that it does track things like touched. This in turn leads to massive confusion and resetForm existing in two places and doing two different things.

In order to reset the state of your form (if you don't want to clear it) you need to call formDirective.resetForm(form.value) and pass in the current value. This is clumsy and can potentially have race conditions if called in the wrong place. At the very least I'm thinking we need resetSubmitted() on the FormGroup directive to make it more explicit what we're doing.

Ideally I'd like for FormGroup to have the concept of 'submitted' (doesn't that seem logical), but I'm sure this is one of those 'we leave it up to the developer to track submitted state' things.

Unfortunately as soon as submitted=true then even markAsUntouched() on the form control won't remove the error - due to the default ErrorStateMatcher. I'm happy to make a custom errorstatematcher when absolutely necessary, but I don't think the common need for resetting a form's submitted state (to hide errors) should be the reason I need one.


Couple notes on my need for this:

I have a login form with validation errors - eg. 'Wrong password / This user doesn't exist'.
If a user corrects their username (maybe they forgot which email they signed up with) I want any previous errors on the password field to immediately be wiped out. While there's all kinds of workarounds in every situation it's far easier if we can just call form.resetSubmitted() and then we all know what that will do.


So a horrible hack to prevent this happening is not to put [FormGroup] on the <form> tag like this:

<form (submit)="formSubmit()">
    <!-- define formGroup separate from the form to prevent submitted being set -->
    <ng-container [formGroup]="form">
      <mat-form-field>

This prevents submitted ever being set on the formGroup directive.

Horrible hack though. This is more for demonstration purposes. You're better off just creating your own ErrorStateMatcher.

And finally note that you should be using (ngSubmit) on a formGroup (on a form tag) which 'rebroadcasts' the submit event from the HTML form.

https://stackblitz.com/edit/angular-material2-issue-13240-ejnf2r


And small final note about @mattrenaud's answer:

  • In your second example, you prevent submitted from ever being set by creating a submit button with type=button - but this has the unfortunate side effect that you can't hit 'Enter' in the password field to login which isn't good. So your first example is better.

All 10 comments

My crystal ball is broken, so a complete stackblitz example would be helpful.
You can use https://goo.gl/wwnhMV as starting point.

@AndonMitev can you share a reproduction on stackblitz?

We've just hit the same issue as @AndonMitev. I have created a reproduction here:
https://stackblitz.com/edit/angular-material2-issue-13240

As you will see there, after you click the Submit button, the form is then reset. This causes the form fields to be emptied out, but the error state still persists on the mat-input components.

I'm not sure if it will help, but I am also logging the form state before and after the reset in the console.

I see. The actual issue is that the MatInput doesn't reset its error state when the NgForm is reset.

I've done some investigation on this one using @fdewet 's example as a starting point. I'm not sure this is an issue.

The problem stems from the submitted property, the button with implied type=submit, and the way the default ErrorStateMatcher handles this case.

The input is revalidating the error state on change detection. But only after the form is "submitted" does the check set the errorState to true. Also, based on my research, keeping the form as "submitted" is normal.

So with that said, shouldn't the example be rewritten to use resetForm:
https://stackblitz.com/edit/angular-material2-issue-13240-7kxxw1?file=app/app.component.ts

OR

rewritten to set type=button and handling the (click):
https://stackblitz.com/edit/angular-material2-issue-13240-7gz9lm?file=app/app.component.html

I've been struggling with this issue the last couple days. The struggle being figuring out exactly what is doing what, and having to delve into way more of the sourcecode than I'd like.

As @mattrenaud said it's ultimately the ErrorStateMatcher that decides whether or not to display a mat-error / mat-form-field control. It's also important to realize that ErrorStateMatcher is part of the angular/material project, and nothing to do with regular angular forms. So studying only the angular forms documentation isn't going to get you a solution if you're using mat-error or material controls.

Another source of confusion for me was that it's the FormGroup directive that tracks submitted and not the FormGroup object that contains the data model. In fact the FormGroup object (where you define controls) doesn't even have the concept of 'submitted'.

Looking at source code reveals that submitted=true is only set in the following scenarios:

  • You have the [FormGroup] defined on an actual <form> control. If you have it on a <div> it will never trigger submittted to be true.
  • You manually call onSubmit(undefined) on the directive.
  • (it is readonly so you can't just set or unset it)

This all caused me a lot of confusion - and I feel like there's a slight disconnect between the angular core and angular material teams in providing a completely integrated model. By this I'm mostly concerned that the FormGroup data model has no concept of submitted, but that it does track things like touched. This in turn leads to massive confusion and resetForm existing in two places and doing two different things.

In order to reset the state of your form (if you don't want to clear it) you need to call formDirective.resetForm(form.value) and pass in the current value. This is clumsy and can potentially have race conditions if called in the wrong place. At the very least I'm thinking we need resetSubmitted() on the FormGroup directive to make it more explicit what we're doing.

Ideally I'd like for FormGroup to have the concept of 'submitted' (doesn't that seem logical), but I'm sure this is one of those 'we leave it up to the developer to track submitted state' things.

Unfortunately as soon as submitted=true then even markAsUntouched() on the form control won't remove the error - due to the default ErrorStateMatcher. I'm happy to make a custom errorstatematcher when absolutely necessary, but I don't think the common need for resetting a form's submitted state (to hide errors) should be the reason I need one.


Couple notes on my need for this:

I have a login form with validation errors - eg. 'Wrong password / This user doesn't exist'.
If a user corrects their username (maybe they forgot which email they signed up with) I want any previous errors on the password field to immediately be wiped out. While there's all kinds of workarounds in every situation it's far easier if we can just call form.resetSubmitted() and then we all know what that will do.


So a horrible hack to prevent this happening is not to put [FormGroup] on the <form> tag like this:

<form (submit)="formSubmit()">
    <!-- define formGroup separate from the form to prevent submitted being set -->
    <ng-container [formGroup]="form">
      <mat-form-field>

This prevents submitted ever being set on the formGroup directive.

Horrible hack though. This is more for demonstration purposes. You're better off just creating your own ErrorStateMatcher.

And finally note that you should be using (ngSubmit) on a formGroup (on a form tag) which 'rebroadcasts' the submit event from the HTML form.

https://stackblitz.com/edit/angular-material2-issue-13240-ejnf2r


And small final note about @mattrenaud's answer:

  • In your second example, you prevent submitted from ever being set by creating a submit button with type=button - but this has the unfortunate side effect that you can't hit 'Enter' in the password field to login which isn't good. So your first example is better.

I have the same bug, the problem is that reset doesn't work with ngSubmit, here is the working demo

https://stackblitz.com/edit/angular-qege7u

Most probably duplicate of #4190, simply use type="reset" on your submit button as #4190 (comment) suggests.

Most probably duplicate of #4190, simply use type="reset" on your submit button as #4190 (comment) suggests. @danieldanielecki

No one has mentioned this here or in #4190 but for this solution to work your button must be within the form tags
```

...

Most probably duplicate of #4190, simply use type="reset" on your submit button as #4190 (comment) suggests. @danieldanielecki

No one has mentioned this here or in #4190 but for this solution to work your button must be within the form tags

<form [formGroup]="form">

...

<button type="reset" (click)="form.reset()">Reset</button>

</form>

That solution of type=reset only seems to be working for everyone but mat-select @_@

Was this page helpful?
0 / 5 - 0 ratings

Related issues

shlomiassaf picture shlomiassaf  路  3Comments

LoganDupont picture LoganDupont  路  3Comments

alanpurple picture alanpurple  路  3Comments

Miiekeee picture Miiekeee  路  3Comments

3mp3ri0r picture 3mp3ri0r  路  3Comments