Explain Codes LogoExplain Codes Logo

Expected validator to return Promise or Observable

javascript
async-validators
sync-validators
custom-validators
Nikita BarsukovbyNikita Barsukov·Sep 12, 2024
TLDR

To ensure your custom validator in Angular is compatible with async operations, it must return either a Promise or an Observable. The returned value should be null for valid input or an error object for invalid input.

Here's the essence in code:

Promise-based validator:

function asyncValidator(control) { return new Promise(resolve => { // Simulate async validation, not a coffee break :) setTimeout(() => { const result = control.value === 'valid' ? null : { error: true }; resolve(result); }, 1000); }); }

Observable-based validator:

import { of } from 'rxjs'; import { delay } from 'rxjs/operators'; function asyncValidator(control) { // Immediate validation result, because we're faster than Flash const result = control.value === 'valid' ? null : { error: true }; return of(result).pipe(delay(1000)); // Simulate async operation }

Use null for no errors and { key: true } for validation issues.

Building validation armies with arrays

The power of arrays

Array of validators in Angular reactive forms is like an all-star superhero team, fighting against invalid data. Validators are those superheroes and you provide an array as their Justice League.

Basic structure of control validation

Angular reactive forms follow a defined structure for form control initialization:

new FormControl(INITIAL_VALUE, [syncValidators], [asyncValidators])

Here, the second argument holds your synchronous validators (e.g., Validators.required), while the third argument holds the asynchronous validators (e.g., your asyncValidator function).

Sync vs. async: What's the rush?

Knowing the distinction between synchronous and asynchronous validators is key:

  • Sync validators: Instant check, because not everyone likes to wait.
  • Async validators: Takes a while, returns a Promise or Observable. Not all heroes wear capes; some just take time!

Validator organization tips

Here’s some quick tips to ward off the villains in your validation process:

  • Combine with compose: When you can't use an array, Validators.compose([..validators]) is your sidekick.
  • Keep sync and async apart: Synchronous validators, then asynchronous ones. Don't make them fight each other!
  • Brackets for groups: One validator can stand solo, but multiple validators need to huddle inside brackets.
  • Syntax snares: Extra commas, missing brackets are spawns of Lex Luthor. Be vigilant!

Custom validators and successful form validation escapes

Your own validator, your own rules

Beyond built-in validators, creating custom synchronous validators is a piece of cake:

// Our custom sync validator function customRequiredValidator(control) { const isValid = control.value !== null && control.value.trim().length > 0; return isValid ? null : { 'required': true }; } // Usage new FormControl('', [customRequiredValidator, anotherSyncValidator])

Just remember the golden rule: For synchronous validators no Promise or Observable needed! Like having cheesecake without the cheese.

Tackling the async world

For validators that need to communicate with the server:

  • Promise: A Promise sees the future once. Like asking a crystal ball a single question.
  • Observable: An Observable provides continuous updates. Like your chatty friend!

Common validation errors and their easy fixes

Unorganized array, unruly errors

One common error is not providing the array of validator warriors. For example, this error:

Error: Expected validator to return Promise or Observable

This yells that a validator was left lonely. Be the good friend, bring them back into the array party.

Guarding against the unexpected

Validators have rules of engagement:

  • Explicit with formState: Always pass an initial value, or it's null by default.
  • Validation checklist: Validation is like going on an adventure, always do a pre-check.
  • Sequence matters: Validators are like dominos, they fall in the right order.