CodeForge

Angular 21 de Cero a Experto / Formularios

Validaciones: integradas, custom y asíncronas

Teoría24 min20 XP

Un formulario sin validación es una puerta abierta a datos corruptos: montos negativos, emails inventados, campos vacíos. Angular trae validadores integrados para lo común y te deja escribir los tuyos para tus reglas de negocio. Hoy dominas las tres capas: validadores de fábrica, validadores CUSTOM (funciones puras que puedes testear en vivo) y validación asíncrona (consultar el servidor). La garantía de que solo entren datos correctos.

El inspector en la puerta

Validadores integrados

Angular incluye los más comunes en la clase Validators:

integrados.ts
import { Validators } from '@angular/forms';

this.fb.group({
nombre:   ['', [Validators.required, Validators.minLength(2)]],
valor:    [0,  [Validators.required, Validators.min(1), Validators.max(10_000_000)]],
email:    ['', [Validators.required, Validators.email]],
telefono: ['', Validators.pattern(/^3\d{9}$/)],   // celular colombiano
});

Cada validador es una función que Angular ejecuta sobre el control. Si el valor no cumple, el control queda inválido y expone el error en control.errors (por ejemplo { required: true } o { min: { min: 1, actual: 0 } }).

Validadores custom: tu propia regla

Un validador es simplemente una función ValidatorFn: recibe el control y devuelve null si todo está bien, o un objeto de error si no. Esa función es TypeScript puro —testeable en aislamiento—:

validador-custom.ts
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';

// regla de negocio: el nombre no puede ser solo espacios
export function noSoloEspacios(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
  const valor = (control.value ?? '') as string;
  return valor.trim().length === 0 ? { soloEspacios: true } : null;
};
}

// úsalo como cualquier validador integrado:
this.fb.group({
nombre: ['', [Validators.required, noSoloEspacios()]],
});

La convención: devolver null = válido; devolver { nombreDelError: ... } = inválido. El patrón "función que devuelve un validador" (una fábrica) permite parametrizarlo —por ejemplo, montoMaximo(limite)—.

Validación a nivel de grupo (entre campos)

Algunas reglas involucran VARIOS campos: "la confirmación de contraseña debe coincidir", "la fecha fin debe ser posterior a la fecha inicio". Esos validadores van en el FormGroup, no en un control:

grupo.ts
export function clavesCoinciden(): ValidatorFn {
return (group: AbstractControl): ValidationErrors | null => {
  const clave = group.get('clave')?.value;
  const confirmar = group.get('confirmar')?.value;
  return clave === confirmar ? null : { noCoinciden: true };
};
}

this.fb.group(
{ clave: ['', Validators.required], confirmar: ['', Validators.required] },
{ validators: clavesCoinciden() },   // ← validador del GRUPO, ve todos los campos
);

Validación asíncrona

Cuando validar requiere consultar el servidor (¿el email ya existe?), usas un validador ASÍNCRONO: devuelve una Promise u Observable del error o null. Angular muestra el estado pending mientras espera:

async.ts
import { AsyncValidatorFn } from '@angular/forms';
import { map } from 'rxjs';

export function emailDisponible(api: ApiService): AsyncValidatorFn {
return (control) =>
  api.existeEmail(control.value).pipe(
    map((existe) => (existe ? { emailTomado: true } : null)),
  );
}

// se pasa como TERCER argumento (validadores async):
email: ['', [Validators.required, Validators.email], [emailDisponible(this.api)]],

Pruébalo: la lógica de validación en vivo

Un validador es una función pura —dado un valor, devuelve el error o null—. Esa es la parte que puedes ejecutar y testear sin Angular. Aquí, varios validadores custom en acción:

Fíjate en combinar: recorre los validadores y devuelve el PRIMER error, o null si todos pasan. Es exactamente lo que hace Angular con el array [Validators.required, min(1)]. La lógica de validación es TypeScript puro y testeable —una de las razones por las que los formularios reactivos escalan tan bien—.

¿Qué debe devolver un validador (`ValidatorFn`) cuando el valor es VÁLIDO?

Mini-reto

En el playground: 1) escribe un validador custom maxLongitud(n) (fábrica) que falle si el texto supera n caracteres; 2) uno empiezaCon(prefijo) para un código de invitación; 3) combínalos con requerido para validar un campo codigo y prueba varios valores. Luego piensa cómo escribirías la versión Angular: codigo: ['', [Validators.required, maxLongitud(8), empiezaCon('MB')]].

Qué sigue

Ya validas los datos. Pero validar es solo la mitad: el usuario necesita VER qué está mal, de forma clara y accesible. La próxima lección cubre los estados del formulario (touched/dirty), cómo mostrar mensajes de error en el momento justo, y cómo hacerlo accesible para lectores de pantalla —un formulario que también se oye—.