Angular Signal Forms: Lleva tus validadores al siguiente nivel
¿Sigues luchando con validaciones complejas en Reactive Forms de Angular? Descubre cómo la nueva API de Signals Forms hace tu código más…
Angular Signal Forms: Lleva tus validadores al siguiente nivel

Angular Signal Forms: Lleva tus Validadores al Siguiente Nivel
¿Sigues luchando con validaciones complejas en Reactive Forms de Angular? Descubre cómo la nueva API de Signals Forms hace tu código más limpio, reactivo y seguro.
Si le preguntamos a cualquier desarrollador de Angular cuál es la parte más tediosa de los formularios, probablemente mencionará el manejo de errores y las validaciones personalizadas. Tradicionalmente, implementar esto implicaba funciones externas y un control manual del estado que podía volverse caótico.
Pero el ecosistema está evolucionando. Con la llegada de Signal Forms (actualmente en fase experimental), Angular nos promete una sintaxis más limpia y una mayor seguridad de tipos.
En este artículo, vamos a migrar un formulario de registro clásico a la nueva API, centrándonos en lo más importante: cómo crear validadores personalizados que sean realmente reactivos.
El experimento: Un formulario de registro
Para entender la potencia de Signals, no necesitamos un formulario gigante, sino uno con reglas claras. Vamos a construir un formulario de ‘Crear Cuenta’ con dos campos:
- Usuario: Requiere validación personalizada (mínimo tres caracteres, solo letras y números).
- Correo electrónico: Validación estándar de formato y campo requerido.
El objetivo es que el botón de envío permanezca deshabilitado hasta que todos los datos estén limpios.

Formulario base de registro
El “viejo mundo#: Reactive Forms
Antes de dar el salto, recordemos cómo lo hacíamos hasta ahora. En Reactive Forms, dependemos del ReactiveFormsModule y vinculamos una directiva [formGroup] en el HTML.
<form [formGroup]="form" autocomplete="off" (ngSubmit)="onSubmit($event)">
La validación personalizada tradicional se ve así: recibimos un AbstractControl, evaluamos el valor y retornamos un objeto de error o null.
Enfoque clásico (Reactive Forms):
/**
* Obtiene el mensaje de error para el campo de nombre de usuario
* según las validaciones aplicadas
*/
protected getUsernameError(): string {
const usernameControl = this.form.get('username');
if (!usernameControl || !usernameControl.errors) {
return '';
}
if (usernameControl.errors['required']) {
return 'Este campo es obligatorio';
}
if (usernameControl.errors['usernameInvalid']) {
return usernameControl.errors['usernameInvalid'];
}
return '';
}
Aunque este enfoque es sólido, tiene un problema: no se integra de forma natural con el nuevo sistema de reactividad de Angular. Aquí es donde entran los Signals.
La revolución: Migrando a Signal Forms
La API de Signal Forms utiliza Signals para gestionar tanto el estado como la validación, ofreciendo un modelo mucho más predecible. Veamos cómo transformar nuestro código en cuatro pasos clave.
1. Simplifica tu modelo
Olvídate de los FormControl. En Signal Forms, tus campos pasan a ser valores primitivos. Primero, definimos una interfaz limpia y creamos una señal que servirá como nuestra “fuente de la verdad”.
interface SignUpForm {
username: string;
email: string;
}
El estado inicial es simplemente una señal:
import { Component, computed, model, signal } from '@angular/core';
/**
* Modelo de datos para el formulario de registro
* por defecto los campos están vacíos y son signals
*/
protected model = signal<SignUpForm>({
username: '',
email: '',
});
2. Define el formulario ‘In-Place’
Ya no necesitamos un FormBuilder complejo. Utilizamos la función form() vinculada a nuestro modelo. Lo genial es que las validaciones se integran directamente en la definición.
/**
* Definición del formulario utilizando signal-forms
* Se aplican las validaciones necesarias a cada campo
*/
protected form = form(this.model, (s) => {
required(s.username);
required(s.email);
email(s.email);
usernameValidator(s.username); // Aplicar la validación personalizada definida
});
3. Crea el validador 2.0
Aquí ocurre la magia. En lugar de recibir un AbstractControl, ahora trabajamos con un SchemaPath. Usamos la función validate() que nos da acceso al contexto del valor.
Fíjate en la diferencia clave: ahora retornamos un objeto con un kind (tipo de error) y un message explícito. Esto elimina la necesidad de mapear códigos de error extraños en el template.
import {
form,
FormField,
required,
email,
validate,
SchemaPath,
submit,
} from '@angular/forms/signals';
/**
* Esta función valida que el nombre de usuario cumpla con ciertos criterios:
* - Solo debe contener caracteres alfanuméricos (letras y números).
* - La longitud debe estar entre 3 y 20 caracteres.
* @param field
*/
function usernameValidator(field: SchemaPath<string>) {
validate(field, (ctx) => {
// Obtener el valor del campo
const value = ctx.value();
if (!value) {
return null; // La validación de requerido se maneja por separado
}
// Solo debe contener caracteres alfanuméricos (letras y números)
if (!/^[a-zA-Z0-9]+$/.test(value)) {
return {
kind: 'usernameInvalid', // Tipo de error personalizado
message: 'Solo debe contener letras y números',
};
}
// La longitud debe estar entre 3 y 20 caracteres
if (value.length < 3 || value.length > 20) {
return {
kind: 'usernameInvalid', // Tipo de error personalizado
message: 'Debe tener entre 3 y 20 caracteres',
};
}
return null;
});
}
4. Errores reactivos con computed
Para mostrar los errores, ya no necesitamos ‘getters’ complejos que verifiquen si el campo es touched manualmente una y otra vez. Podemos crear una señal computada (computed) que reaccione automáticamente a los cambios en la colección de errores.
/**
* Obtiene el mensaje de error para el campo de nombre de usuario
* según las validaciones aplicadas
* Se muestran mensajes específicos para cada tipo de error en el html
*/
protected getUsernameError = computed(() => {
// Obtener los errores del campo de nombre de usuario
const errors = this.form.username().errors();
// Verificar si hay un error de requerido
const required = errors.find((e) => e.kind === 'required');
if (required) {
return 'Este campo es obligatorio';
}
// Verificar si hay un error de validación personalizada
const invalid = errors.find((e) => e.kind === 'usernameInvalid'); // tipo definido en usernameValidator en KIND
if (invalid) {
return invalid?.message;
}
// Si no hay errores, retornar cadena vacía
return '';
});
Actualizando la vista (Template)
En el HTML, el cambio es drástico, pero positivo. Decimos adiós a formControlName y damos la bienvenida a la directiva [formField] de ‘@angular/forms/signals’, que conecta el input directamente con la señal.
<!-- Se crea variable para el campo de nombre de usuario y su estado de error -->
@let username = form.username();
@let showUsernameError = username.invalid() && username.touched();
<label for="username">Nombre de usuario</label>
<!-- Campo de entrada para el nombre de usuario, donde
se enlaza el formField desde Signals Forms y se aplica
la clase de error si es necesario -->
<input
type="text"
id="username"
[formField]="form.username"
[class.error]="showUsernameError"
(blur)="touched.set(true)"
class="form-input"
autocomplete="none"
autofill="none"
/>
Al probarlo, verás que el inspector de señales muestra cómo los estados touched, pending e invalid se recalculan en tiempo real. Es transparencia total en el flujo de datos.
Conclusión
Migrar a Signal Forms no es solo un cambio de sintaxis. Es un cambio de mentalidad. Aunque la API sigue en estado experimental, los beneficios son claros:
- Menos código boilerplate: Adiós a las inyecciones innecesarias.
- Reactividad real: Todo, desde el valor hasta el error, es una señal.
- Tipado fuerte: TypeScript te protege mejor que nunca.
Si ya sabías crear validadores en Reactive Forms, la curva de aprendizaje aquí será mínima. ¿Te animas a probarlo en tu próximo prototipo?
Referencias y recursos
Equipo de Angular. (s. f.). Formularios basados en Signals (experimental). GitHub. https://github.com/angular/angular/tree/main/packages/forms/signals
Equipo de Angular. (s. f.). Signals: Documentación oficial. https://angular.dev/guide/signals
Equipo de Angular. (s. f.). Signal Forms: Guía esencial. https://angular.dev/essentials/signal-forms
Jaramillo, F. (s. f.). Forms con Reactive Forms: repositorio de código fuente. GitHub. https://github.com/fernando-jaramillo-pragma/signal-forms-angular/tree/reactive-forms
Jaramillo, F. (s. f.). Forms con Signals: repositorio de código fuente. GitHub. https://github.com/fernando-jaramillo-pragma/signal-forms-angular/tree/validator-signal-form
메타데이터
- post_id
- f8b84c2ecd83
- slug
- angular-signal-forms-lleva-tus-validadores-al-siguiente-nivel-f8b84c2ecd83
- url
- https://medium.com/somos-pragma/angular-signal-forms-lleva-tus-validadores-al-siguiente-nivel-f8b84c2ecd83
- canonical_url
- https://medium.com/somos-pragma/angular-signal-forms-lleva-tus-validadores-al-siguiente-nivel-f8b84c2ecd83
- author_url
- https://medium.com/@fernando.jaramillo.pragma
- status
- ok
- fetched_at
- 2026-07-12 00:07:18