Andrew
Andrew

Reputation: 401

Angular 6 + Angular Material - Form validation on submit

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! ;)

Upvotes: 2

Views: 18771

Answers (1)

Amir Fawzy
Amir Fawzy

Reputation: 468

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

Upvotes: 8

Related Questions