tezvyn:

Angular Custom Validators: Beyond Built-in Rules

AI-drafted, machine-checkedSource: angular.devadvanced
Angular Custom Validators: Beyond Built-in Rules

Custom validators let you define your own business logic for form inputs. Use them to check for unique usernames via an API or enforce complex password rules. The main footgun is forgetting async validators must return an Observable or Promise.

WHY IT EXISTS Built-in validators cover common cases like required or email, but real applications need unique business rules. Custom validators provide a structured way to enforce any logic, from simple format checks to complex, server-side validation, ensuring data integrity specific to your domain.

THE MENTAL MODEL Think of a custom validator as a bouncer for your form field. It's a function you write that inspects the input's value. If the value is good, the bouncer returns null (lets it pass). If it's bad, the bouncer returns an error object (turns it away), which Angular then uses to mark the field as invalid and display an error message.

HOW IT WORKS A custom validator is a function that receives an AbstractControl as its argument. For synchronous validation, the function inspects control.value. If the value is valid, it returns null. If invalid, it returns an object, like { myCustomError: true }. This object is added to the control's errors property. For asynchronous validation, like checking a username against a database via an HTTP call, the function's signature changes: it must return an Observable or a Promise that eventually emits or resolves to null or an error object.

WHEN TO USE IT Use custom validators for any logic not covered by Angular's standard set. This is common for business-specific rules. Three key places this shows up: first, cross-field validation, like ensuring a 'confirm password' field matches the 'password' field; second, asynchronous checks, like verifying a coupon code against an API; third, complex format rules that go beyond a simple regex pattern.

WHEN NOT TO USE IT Don't reinvent the wheel. If a built-in validator like Validators.required, Validators.email, or Validators.pattern can do the job, use it. Overusing complex custom logic for simple checks can make forms harder to read and maintain. For purely display-related transformations of data, a Pipe is often a more appropriate tool than a validator.

ONE CANONICAL EXAMPLE To create a validator that forbids a specific word, you'd write a factory function: function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { const forbidden = nameRe.test(control.value); return forbidden ? { forbiddenName: { value: control.value } } : null; }; }. You then apply it to a form control's validators array: new FormControl('', [Validators.required, forbiddenNameValidator(/test/i)]).

Read the original → angular.dev

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.