10

I´m working on this form and want to validate while typing. The current behavior it´s that I select the input, type and when I click on other site then the error it´s showing. I think the error it's happening when i set control, valid and dirty, but i can't figure it out.

Typescript

buildForm(): void {
 this.userForm = this.fb.group({
  'email': ['', [
    Validators.required,
    Validators.pattern('[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,3}$')
  ]
  ],
  'password': ['', [
    Validators.required,
    Validators.minLength(6),
    Validators.maxLength(25)
   ]
   ],
 });
 this.userForm.valueChanges.subscribe(data => this.onValueChanged(data));
}

onValueChanged(data?: any) {
 if (!this.userForm) { return; }
 const form = this.userForm;
 for (const field in this.formErrors) {
  // clear previous error message (if any)
  this.formErrors[field] = '';
  const control = form.get(field);
  if (control && control.invalid && control.dirty) {
    const messages = this.validationMessages[field];
    for (const key in control.errors) {
      this.formErrors[field] += messages[key] + ' ';
     }
   }
  }
}

Function onValueChanged() change this object

formErrors = {
  'email': '',
  'password': ''
};

And this object has the validation messages.

validationMessages = {
 'email': {
  'required': 'Email is required',
  'pattern': 'Email is invalid'
 },
'password': {
  'required': 'Password is required',
  'minlength': 'Debe tener 6 caracteres como mínimo',
  'maxlength': 'Password cannot be more than 40 characters long.',
 }
};

HTML

      <mat-form-field class="example-full-width">
          <input matInput placeholder="Email" formControlName="email" required>
          <mat-error *ngIf="formErrors.email" align="start" class="form__error">
            {{ formErrors.email }}
          </mat-error>
        </mat-form-field>     
        <mat-form-field class="example-full-width">
          <input matInput placeholder="Password" type="password" formControlName="password" required>
          <mat-error *ngIf="formErrors.password" align="start" class="form__error">
            {{ formErrors.password }}
          </mat-error>
        </mat-form-field>

4 Answers 4

13

Late answer, but I'll post it there in case it may help anyone.

With Angular 6+ you could adjust form validation behavior via onChange FormControl option attribute e.g.:

'email': ['', {
    validators: [
        Validators.required,
        Validators.pattern('[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,3}$')
    ],
    updateOn: 'change'
 }]

updateOn can be:

change - validate immediately, on change

blur - validate when navigating out from the field

submit - validate on form submit

Sources:
https://angular.io/api/forms/AbstractControlOptions
https://angular.io/guide/form-validation#note-on-performance

(I've just answered similar question here: Fire validation when focus out from input in angular?)

Sign up to request clarification or add additional context in comments.

Comments

2

By default, the form field errors will appear when a control is touched or the form is submitted and the control is invalid. To change that behavior, you can use a custom ErrorStateMatcher - see the Angular Material example Input with a custom ErrorStateMatcher.

Comments

0

In template driven forms you can use ngModelOptions

<input matInput [(ngModel)]="name" required [ngModelOptions]="{ updateOn: 'change' }" name="Name" />

Comments

0

The way I found to resolve this issues is using the ShowOnDirtyErrorStateMatcher that comes in Angular Material (at least for version 20+). If you use it as errorStateMatcher of the matInput it solves the problem:

import { ShowOnDirtyErrorStateMatcher } from '@angular/material/core';

@Component({
    ...
})
export class YourComponent {
    showOnDirtyStateMatcher = new ShowOnDirtyErrorStateMatcher()
}
<!-- on your template -->
<input matInput [errorStateMatcher]="showOnDirtyStateMatcher">

Also I recommend using it as a default configuration for all matInputs. For doing this you have to put this code in the angular main providers:

import { ErrorStateMatcher, ShowOnDirtyErrorStateMatcher } from '@angular/material/core';

bootstrapApplication(AppComponent, {
  providers: [
    { provide: ErrorStateMatcher, useClass: ShowOnDirtyErrorStateMatcher } // <-- this
  ]
}

Note: this is for Angular 17+ which use standalone approach. If you still using modules I think you can also add this as provider in your app.module file.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.