How to set a custom error message to a form in angular
Asked Answered
Z

8

21

I have the below code in my component:

 if (form.controls['minRange'].hasError('min')) {
        form.controls['minRange'].setErrors({ min: true });
 }

I am getting the message in input as 'minRange is invalid'.But I want to display the error message as 'P

'Please enter a 5 digit value'

Like .setErrors how can I set errormessage in formcontrol.Can any one please help me.Thanks.

Zenda answered 31/1, 2020 at 8:14 Comment(0)
P
18

Reactive Form in Angular allows you to have custom errors and custom validations. Below is an example:

HTML : Validate if the input is number.

<form  [formGroup]="form">
  <input formControlName="age" >
  <span *ngIf="form.controls.age.errors?.invalidNumber">   // <--- invalidNumber is the key
      Age should be a number
  </span>
</form>

Component.ts

 class TestComponent implements OnInit{
  form:FormGroup;

  ngOnInit(){
    this.createForm();
    this.listenToAgeChange();
  }

  createForm(){
    this.form = new FormGroup({
      age:new FormControl(null)
    })
  }

  listenToAgeChange(){
    this.form.controls.age.valueChanges.subscribe(age=>{
      if(isNaN(age)){
        this.form.controls.age.setErrors({invalidNumber:true})  // <--- Set invalidNumber to true 
      }else{
        this.form.controls.age.setErrors(null)
      }
    })
  }
}
Psalterium answered 7/3, 2020 at 14:25 Comment(0)
B
17

2021/22+ Answer. Don't access form control/error directly. Now there are functions to do so.

In HTML something like this in combination with required.

This is our custom error key.

loginForm.get('email')?.getError('invalid')

<span
      *ngIf="
        !loginForm.get('email')?.valid &&
        (loginForm.get('email')?.touched || loginForm.get('email')?.dirty)
      "
    >
      {{
        loginForm.get('email')?.hasError('required')
          ? 'Username Required'
          : loginForm.get('email')?.getError('invalid')
      }}

</span>

In Component when error occurs in API call

 this.loginForm.get('email')?.setErrors({ invalid: 'Invalid Username' });

'invalid' is key, it's value is our error message.

That's it!

Bank answered 12/4, 2021 at 14:29 Comment(0)
Z
8

TS:

if (form.controls['minRange'].hasError('min')) {
   form.controls['minRange'].setErrors({ incorrect: true, message: 'Please enter a 5 digit value'});   
}

html:

<ion-text color="danger">{{form.controls['minRange'].errors.message}}</ion-text>
Zonda answered 22/12, 2020 at 12:46 Comment(0)
B
3

This is my implementation.

File: src/app/app.component.ts

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  genders = ['male', 'female'];
  signupForm: FormGroup;
  forbiddenUsernames = ['Chris', 'Anna'];

  ngOnInit() {
    this.signupForm = new FormGroup({
      'userData': new FormGroup({
this.checkForbiddenNames.bind(this)]),
        'username': new FormControl(null, [Validators.required, this.checkNoBlankSpaces, this.checkForbiddenNames.bind(this)]),
        'email': new FormControl(null, [Validators.required, Validators.email]),
      }),
      'gender': new FormControl('male'),
      'hobbies': new FormArray([]),
    });
  }

  onSubmit(): void {
    console.log(this.signupForm);
  }

  getControls() {
    return (<FormArray>this.signupForm.get('hobbies')).controls;
  }

  onAddHobby(): void {
    const control = new FormControl(null, Validators.required);
    (<FormArray>this.signupForm.get('hobbies')).push(control);
  }

  checkForbiddenNames(control: FormControl): { [key: string]: boolean } {
    if (this.forbiddenUsernames.map(user => user.toLowerCase()).includes(control.value?.toLowerCase())) {
      return {'nameIsForbidden': true};
    }
  }

  checkNoBlankSpaces(control: FormControl): { [key: string]: boolean } {
    const nonWhitespaceRegExp: RegExp = new RegExp('\\S');
    if (!nonWhitespaceRegExp.test(control.value) && control.value.length > 0) {
      return {'onlyWhiteSpaces': true};
    }
  }

  usernameCustomErrorMessages(): string {
    if (this.signupForm.get('userData.username').errors?.nameIsForbidden) {
      return 'The given username is not available';
    } else if (this.signupForm.get('userData.username').errors?.onlyWhiteSpaces) {
      return 'The given username can not be blank spaces';
    } else {
      return 'Please type a username';
    }
  }
}

File: src/app/app.component.html

          <div class="form-group">
            <label for="username">Username</label>
            <input
              type="text"
              id="username"
              name="username"
              class="form-control"
              formControlName="username"
            >
          </div>
          <span *ngIf="(signupForm.get('userData.username').invalid && signupForm.get('userData.username').touched); else usernameOk" class="help-block">
            <span>{{usernameCustomErrorMessages()}}</span>
          </span>
          <ng-template #usernameOk><span class="help-block">&nbsp;</span></ng-template>
Bochum answered 16/6, 2022 at 18:58 Comment(2)
I tried this method but when I put a log inside the function, this function executed over and over again.Altruist
Nice and useful answer!Mencher
M
1

I have seen this pattern of ngIf inside the html template numerous times and I have got to say it's not that scalable, Just think if you have to customize many error messages on a lot of inputs with many validators. It would have been nice if Angular would have let to set the error message on the validator. Then you would just use a pipe to print all the errors and be done with.

My advice if you have just one simple form you can use this pattern but if not try to think of something more centralized that can scale.

Here is an example for a pipe that can display your errors

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({
  name: 'inputError'
})
export class InputErrorPipe implements PipeTransform {

  transform(value: any, ...args: any[]): string {
    let entered:boolean=false;
    let rvalue: string='';
    if(value){
      Object.values(value).forEach((message) => {
        if(typeof message !== 'undefined'){
          if (entered) {
            rvalue += '\n ';
          }
          entered=true;
          rvalue += message;
        }
      });
    }else{
      rvalue='';
    }
    return rvalue;
  }

}
Mcmillen answered 1/2, 2021 at 7:7 Comment(0)
P
0
 <form  [formGroup]="form">
      <input formControlName="userName" > 
 <div *ngIf="form.controls['userName'].errors?.minlength">
         Please enter a 5 digit value
    </div> 
    </form>

under ts file you need to create FormGroup with validators 1.declare

 form:FormGroup
  1. initialize form inside ngOninit

    this.form  = this.formBuilder.group({
           userName: ['', Validators.minLength(5)],
         });
    
  2. import statement

    import { FormBuilder, FormGroup, Validators  } from '@angular/forms'; 
    
Pagurian answered 31/1, 2020 at 10:11 Comment(0)
T
-2

It's set in the HTML template. I suspect that you have a code there which renders the said message.

Try:

  <input ...your input here ...>
  <div *ngIf="formcontrol.min">
    Please enter a 5 digit value
  </div>

if you need custom form validation, maybe you can also give a try to custom form validators.

Tl answered 31/1, 2020 at 8:19 Comment(0)
C
-2

To validate minimum length in reactive form we can use minlength attribute with formControlName as following:

<form  [formGroup]="form">
  <input formControlName="userName" minlength="5"> < ---
</form>
<div *ngIf="userName.errors?.minlength">
     Please enter a 5 digit value
</div> 

we need to use Validators.minLength with FormControl while creating FormGroup. app.component.ts:

 form = new FormGroup({
    userName: new FormControl('', Validators.minLength(5)) < ---
  });

  get userName() {
    return this.form.get('userName');
} 

https://stackblitz.com/edit/angular-ngvbzn?file=src%2Fapp%2Fapp.component.html

Hope it helps!

Conglobate answered 31/1, 2020 at 8:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.