DEV Community

Marko Berger
Marko Berger

Posted on

Angular custom validators and some weird ones

After some time I'm at it again. I want to share something with YOU. So that you don’t bang your head on the table as I have. I'm working on an angular & angular material project. So we have basic validation on a numeric input field.

<div class="example-container">
  <mat-form-field>
    <input matInput 
      type="number" 
      placeholder="Some number ..."  
      [(ngModel)]="someNumber" 
      #someVal="ngModel"
      min="0" max="10">

  </mat-form-field>

</div>

Nothing special here. Right? We want to enter a value from 0 to 10. If we use arrows on the control, no problem. But we can input value by hand. So if we enter -2. We would expect to see mat-error message. Wrong. Validator min is not registering that -2 is lower than 0 and by that, it is not throwing an invalid. The same thing is happening if we enter 12. Why?
I don’t know. But I really think it is an issue with min and max validators.

Why should it be easy when it can be complicated.

We need to write some validators.
First, we are going to write a top border value validator.

import { Directive, Input } from '@angular/core';
import { NG_VALIDATORS, Validator, ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms';

@Directive({
  selector: '[maxValueValidator]',
  providers: [{provide: NG_VALIDATORS, useExisting: MaxValueValidatorDirective, multi: true}]
})
export class MaxValueValidatorDirective implements Validator{
  // our limit value
  @Input('maxValueValidator') maxValue: string;
  validator: ValidatorFn; 
  numValue: number; 
  constructor() { this.numValue = +this.maxValue}
  //needed by the Validator interface
  validate(control: AbstractControl): ValidationErrors {
    return this.maxValueValidator()(control);
  }

  // If value is valid it will return null 
  // if it's not it will return {'maxValueValidator': { value: control.value}}
  maxValueValidator(): ValidatorFn {
    return (control: AbstractControl): {[key: string]: any} | null => {
      return control.value > this.maxValue ? {'maxValueValidator': { value: control.value}} : null;
    };
  }
}

Simple right. (Now you can write the lower border value) .
Custom validators are cool! Right.

<div class="example-container">
  <mat-form-field>
    <input matInput 
      type="number" 
      placeholder="Some number ..."  
      [(ngModel)]="someNumber" 
      #someVal="ngModel"
      min="0" max="10" minValueValidator="0" maxValueValidator="10">
    <mat-error *ngIf="someVal.hasError('minValueValidator')">Number must be positive value!</mat-error>
    <mat-error *ngIf="someVal.hasError('maxValueValidator')">Number must be under 10!</mat-error>
  </mat-form-field>

</div>

You can view and try this example here. Enjoy.

Top comments (1)

Collapse
 
ribizlim profile image
Mark Magyarodi • Edited

just to clarify why min and max are not working: they're not existing angular directives. they're just HTML5 input attributes to limit the stepper (also works with HTML5 native validation).
angular however provides these validators for reactive forms. (you can also use them in your directives)