Angular2 reactive forms show error messages based on validation fail condition

sensei picture sensei · Oct 29, 2017 · Viewed 36.5k times · Source

I want to mention that i am using separate generic component, to display errors, so I am not putting them directly inside of html to avoid repetition, so I can't chain and hardcode conditional directly in template.

I have this field and two validations applied:

this.username = new FormControl('', [ Validators.minLength(5), Validators.required ]); 

How do I show validation error message for each of validations? Let's say I want to show both errors if nothing is in field on submit:
"Minimum length is 5"
"Field is required"

And then if you put something inside it should only show:
"Minimum length is 5"

This is an example but, my real life example is comparing two email fields, if they are the same, so if email is not correct is should show:
"Email is not correct"
"Email is not the same as first email field"

So if email is correct and emails are not the same should only show:
"Email is not the same as first email field"

I have validations working if not passed, but I am not sure how to show separately if one validation passed and another one not, because I have to properly append the real reason why it failed and not put some generic reason for both.

Full example:

Component for error display:

import { Component, OnInit, Input } from '@angular/core';

@Component({
  selector: 'app-field-error-display',
  templateUrl: './field.error.display.component.html',
  styleUrls: ['./field.error.display.component.css']
})
export class FieldErrorDisplayComponent
{
    @Input() errorMsg: string;
    @Input() displayError: boolean;
}

Component html:

<div *ngIf="displayError" >
  <small>{{errorMsg}}</small>
</div>

Usage of erorr display in Register.html:

<form [formGroup]="_form" (ngSubmit)="register()">
<h4>Create a new account</h4>
<div class="form-group">
  <label for="email">Email Address</label>
  <input class="form-control" name="email" formControlName="email" />
  <app-field-error-display [displayError]="formValidationService.IsFieldValid(_form,'email')" errorMsg="Please insert correct email"></app-field-error-display>
</div>
<div class="form-group">
  <label for="emailConfirm">Email Address Confirm</label>
  <input class="form-control" name="emailConfirm" formControlName="emailConfirm" />
  <app-field-error-display [displayError]="formValidationService.IsFieldValid(_form,'emailConfirm')" errorMsg="Please insert correct email"></app-field-error-display>
</div>
<div class="form-group">
  <label for="password">Password</label>
  <input class="form-control" name="password" formControlName="password" />
  <app-field-error-display [displayError]="formValidationService.IsFieldValid(_form,'password')" errorMsg="Please insert password"></app-field-error-display>
</div>
<button type="submit" class="btn btn-default">Create Account</button>
</form>
<div *ngIf="_registered" class="alert alert-success box-msg" role="alert">
  <strong>Account registered!</strong> You will be redirected in sec
</div>

Register component:

ngOnInit() {
    this._form = this._formBuilder.group({
        email: ['', [Validators.required, Validators.email]],
        emailConfirm: ['', [Validators.required, Validators.email, MatchOtherValidator.Validate('email')]],
        password: ['', [Validators.required]]
    });

    this._registerModel = new RegisterModel();
}

Custom match other validator:

import { FormControl } from '@angular/forms';

export class MatchOtherValidator {
    static Validate(otherControlName: string) {

        let thisControl: FormControl;
        let otherControl: FormControl;

        return function matchOtherValidate(control: FormControl) {

            if (!control.parent) {
                return null;
            }

            // Initializing the validator.
            if (!thisControl) {
                thisControl = control;
                otherControl = control.parent.get(otherControlName) as FormControl;
                if (!otherControl) {
                    throw new Error('matchOtherValidator(): other control is not found in parent group');
                }
                otherControl.valueChanges.subscribe(() => {
                    thisControl.updateValueAndValidity();
                });
            }

            if (!otherControl) {
                return null;
            }

            if (otherControl.value !== thisControl.value) {
                return {
                    matchOther: true
                };
            }

            return null;

        }
    }
}

Form validation service has this method:

public IsFieldValid(form: FormGroup, field: string){
    return !form.get(field).valid && form.get(field).dirty;
}

Answer

JB Nizet picture JB Nizet · Oct 29, 2017

Your code only checks if the form control is dirty and invalid. But you want to check if a specific validation fails. So you need to pass an additional error argument ('required', 'minlength', etc.), and use

form.get(field).hasError(error)