Angular 21 de Cero a Experto / Formularios
Formularios reactivos tipados: FormGroup y FormControl
El corazón de los formularios reactivos son tres piezas: FormControl (un campo),
FormGroup (un conjunto de campos) y FormBuilder (una fábrica que los crea con menos
ruido). Y en Angular moderno, todo eso es TIPADO: el valor de tu formulario no es un any,
es un objeto con la forma exacta que definiste. Hoy montas formularios reactivos con tipos
sólidos y aprendes a leer y reaccionar a sus valores.
Las tres piezas
FormControl: un solo campo
Un FormControl representa un campo: su valor, su estado y sus validadores.
import { FormControl, Validators } from '@angular/forms';
const monto = new FormControl(0, [Validators.required, Validators.min(1)]);
monto.value; // 0 (tipado como number | null)
monto.valid; // false (0 no cumple min(1))
monto.setValue(4500);
monto.valid; // trueFormGroup y FormBuilder: el formulario completo
Un FormGroup agrupa varios controles. En vez de instanciarlos a mano, FormBuilder (con su
método group) lo hace más limpio:
import { Component, inject } from '@angular/core';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
@Component({
selector: 'app-nuevo-gasto',
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="guardar()">
<input formControlName="nombre" placeholder="Nombre" />
<input formControlName="valor" type="number" placeholder="Monto" />
<select formControlName="categoria">
<option value="comida">Comida</option>
<option value="transporte">Transporte</option>
</select>
<button [disabled]="form.invalid">Guardar</button>
</form>
`,
})
export class NuevoGastoComponent {
private fb = inject(FormBuilder);
form = this.fb.group({
nombre: ['', Validators.required],
valor: [0, [Validators.required, Validators.min(1)]],
categoria: ['comida', Validators.required],
});
guardar() {
if (this.form.invalid) return;
console.log(this.form.value); // { nombre, valor, categoria }
}
}Cada campo es [valorInicial, validadores]. El template los conecta con formControlName,
que empareja por el nombre de la clave. form.value te da el objeto completo; form.invalid,
form.valid, form.dirty te dan el estado del conjunto.
Formularios tipados: el gran avance
Leer y reaccionar a los cambios
Tienes tres formas de observar el formulario, de la más simple a la más reactiva:
// 1. valor puntual (al enviar, por ejemplo)
this.form.value; // objeto actual
// 2. como Observable (reacciona a cada cambio) — lo verás en el módulo de RxJS
this.form.valueChanges.subscribe((v) => console.log(v));
// 3. como SEÑAL (moderno): convierte valueChanges a signal para el resto de tu app
readonly valores = toSignal(this.form.valueChanges, { initialValue: this.form.value });
// ahora 'valores()' es reactivo y encaja con computed/effectEsa tercera opción —toSignal— es el puente entre el mundo RxJS de los formularios y el
mundo de señales del resto de tu app. La verás a fondo en el módulo de RxJS; por ahora, basta
saber que el valor de un formulario reactivo puede alimentar un computed como cualquier
otra señal.
En un formulario tipado, ¿qué ocurre si escribes `form.controls.nombre` (con un typo)?
Mini-reto
Diseña el FormGroup de un formulario de perfil de usuario con FormBuilder: campos
nombre (requerido), edad (número, mínimo 18) y email (requerido, formato email).
Escribe: 1) la definición del form; 2) qué forma tendría form.value (con sus tipos);
3) cómo comprobarías si el formulario es válido antes de enviar. Identifica cuál campo daría
error de compilación si lo escribieras mal en el template.
Qué sigue
Tienes la estructura del formulario, pero la validación fue superficial (required, min).
La próxima lección profundiza en las validaciones: los validadores integrados, cómo escribir
validadores CUSTOM (tu propia regla de negocio) y validación asíncrona. El sistema que
garantiza que los datos que entran son correctos.