angularvalidationangular-reactive-forms

Angular Validators.pattern() not working with type="number", works with type="text"


I've got the following input element for entering (part of) a phone number in an Angular 18 app with the associated Validators.pattern(/^[1-9]\d{5,}/):

  ...
  form = this.formBuilder.group({
    someSection: this.formBuilder.group({
      phone: ['', [Validators.required, Validators.pattern(/^[1-9]\d{5,}/)]],
  ...
...
<input type="text" formControlName="phone" placeholder="Phone" />
...

I require that

The above code and pattern works, but having type="text" for the input allows entering characters. When I change it to type="number" I can't enter characters as expected, but then my pattern validation fails - if I enter six or more digits with a leading zero, the control validates without error.

Is there a way to make this work by combining type="number" and the needed validation pattern? Or will I have to manually prevent entering of non-numeric input?


Solution

  • The solution to allow only numbers, is to listen for keypress event and ensure that number keyCodes alone return true, else return false.

    You should prefer type="string" since your inputs accept zero at the beginning, which type="number" does not consider and omits it.

    SO Answer - Prevent user from typing non-number input

    import { Component, inject } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    import { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms';
    import { JsonPipe } from '@angular/common';
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [JsonPipe, ReactiveFormsModule],
      template: `
        <form [formGroup]="form">
          <input type="string" formControlName="phone" placeholder="Phone" (keypress)="IsNumeric($event)"/>
        </form>
    
        {{form.controls.phone.errors | json}}
      `,
    })
    export class App {
      formBuilder = inject(FormBuilder);
      form = this.formBuilder.group({
        phone: ['', [Validators.required, Validators.pattern(/^[1-9]\d{5,}/)]],
      });
      specialKeys: Array<any> = [];
    
      IsNumeric(e: any) {
        const keyCode = e.keyCode == 0 ? e.charCode : e.keyCode;
        return (keyCode >= 48 && keyCode <= 57) ||
        (this.specialKeys.indexOf(e.keyCode) != -1 && e.charCode != e.keyCode);;
      }
    }
    
    bootstrapApplication(App);
    

    Stackblitz Demo