Angular 6 + Angular Material - Form validation on submit

18,024

HTML
wrap all your inputs in form tag

instead

<form #stepOneForm="ngForm">

do

<form (ngSubmit)="onSubmit()" [formGroup]="myForm"></form>

instead

<input matInput placeholder="Name" [formControl]="name" [errorStateMatcher]="matcher">

do

<input matInput placeholder="Name" formControlName="name">

that goes with all input formControlName not [formControl]

instead

<button type="submit [disabled]="!stepOneForm.form.valid" mat-button matStepperNext>Go to next step</button>

do

<button type="submit" [disabled]="myForm.invalid" mat-button matStepperNext>Go to next step</button>

--when you trying to show error msg to access to input errors validation
instead

name.hasError('required')

do

myForm.get('name').errors?.required
//or
myForm.get('name').errors['required']

both ways to check error gonna work the main different between them using safe navigation operator (?.) that's like you saying "hay angular check first if there's error (not unll or undefined) then check the type of error required, maxLength...etc" main purpose to prevent javascript from raising error cannot read property for reference safe navigation operator

or (another validation case)

*ngIf="myForm.get('name').invalid && myForm.get('name').touched"


TS

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
  selector: '...',
  templateUrl: '...',
  styleUrls: ['...']
})
export class myApp implements OnInit {
  myForm: FormGroup;

  ngOninit() {
    this.myForm = new FormGroup({
      'name': new FormControl(null, Validators.required),
      'policyNo': new FormControl(null, validators.minLength(5))
      // the rest of inputs with the same approach
    });
  }

  onSubmit() {
    // when submit the form do something
  }
}

you using here reactive forms not template driven each has different use make sure you use the correct way for both because you mess the reactive approach with template driven approach that what messed up everything. recommendation read Reqctive Froms Template Forms

Share:
18,024
Andrew
Author by

Andrew

Digital solution team member of System Architecture department of an insurance company in Japan.

Updated on June 14, 2022

Comments

  • Andrew
    Andrew almost 2 years

    I have some input fields with different validation rules set on them. But I also want to add some validation to all the fields at once, when submitting, or make submit button disabled, if there are any errors above.

    However, I haven't put all my inputs inside form tag from the beginning and have kind of trouble with that right now. I am new to all of this, so can you please help me? Is there any way to solve it, without recreating all the form? :(

    I've tried adding:

      <form #stepOneForm="ngForm">
    
      <button type="submit [disabled]="!stepOneForm.form.valid" mat-button matStepperNext>Go to next step</button>
    

    But it didnt help...

    My code looks as below:

    HTML

          <!-- Name -->
          <mat-form-field class="dcp-input-field">
            <input matInput placeholder="Name" [formControl]="name" [errorStateMatcher]="matcher">
            <mat-hint>Please enter your name</mat-hint>
    
            <mat-error *ngIf="name.hasError('required')">
              This is <strong>required</strong> field
            </mat-error>
          </mat-form-field>
    
          <!-- DoB -->
          <mat-form-field class="dcp-input-field">
            <input matInput [matDatepicker]="dp" placeholder="Date of Birth" [formControl]="dob" [errorStateMatcher]="matcher">
            <mat-datepicker-toggle matSuffix [for]="dp"></mat-datepicker-toggle>
            <mat-datepicker #dp></mat-datepicker>
            <mat-hint>Please enter your date of birth</mat-hint>
    
            <mat-error *ngIf="dob.hasError('required')">
              This is <strong>required</strong> field
            </mat-error>
          </mat-form-field>
    
          <!-- PolicyNo -->
          <mat-form-field class="dcp-input-field">
            <input matInput placeholder="Policy number" [formControl]="policyNo" [errorStateMatcher]="matcher">
            <mat-hint>Please enter your Policy number</mat-hint>
    
            <mat-error *ngIf="policyNo.hasError('required')">
              This is <strong>required</strong> field
            </mat-error>
            <mat-error *ngIf="policyNo.hasError('minlength')">
              The value is <strong>too short</strong>
            </mat-error>
          </mat-form-field>
    

    TS

     export class MyErrorStateMatcher implements ErrorStateMatcher {
       isErrorState(
         control: FormControl | null,
         form: FormGroupDirective | NgForm | null
       ): boolean {
         const isSubmitted = form && form.submitted;
         return !!(
           control &&
           control.invalid &&
           (control.dirty || control.touched || isSubmitted)
         );
       }
     }
    
    
      name = new FormControl("", [Validators.required]);
      dob = new FormControl("", [Validators.required]);
      policyNo = new FormControl("", [
        Validators.required,
        Validators.minLength(6)
      ]);
    
      matcher = new MyErrorStateMatcher();
    

    Thank you and sorry for noob question! ;)

    • Andrew
      Andrew over 5 years
      I've also tried to insert all my FormControls into one FormGroup, but all the single verifications have broken after that, so looks like I am doing something wrong again...
    • Suryan
      Suryan over 5 years
      Do you want a single form on multi step of material stepper and want to disable button until the form is valid?
    • Andrew
      Andrew over 5 years
      Actually, I want to have several forms divided by steps, and yes - I want to disable "Go to next step" button if there are some errors. So it is not necessary to set validation to overall process if all the steps are already validated.