CodeForge

Angular 21 de Cero a Experto / Formularios

Formularios reactivos tipados: FormGroup y FormControl

Teoría22 min20 XP

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.

control.ts
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;      // true

FormGroup 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:

nuevo-gasto.component.ts
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:

reaccionar.ts
// 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/effect

Esa 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.