Template-driven
El estado vive principalmente en el HTML, con ngModel. Es rápido para formularios
muy simples, pero el tipo de los datos es difícil de rastrear desde el componente.
Hasta ahora todos sus componentes solo leían datos. Un formulario es la primera vez que un componente captura algo que escribe el usuario, lo valida, y solo entonces lo envía. Angular necesita una forma de representar ese estado — lo que el usuario ha escrito, si es válido, si cambió — como algo que el código pueda inspeccionar.
Template-driven
El estado vive principalmente en el HTML, con ngModel. Es rápido para formularios
muy simples, pero el tipo de los datos es difícil de rastrear desde el componente.
Reactive (el que usa este curso)
El estado vive en el componente, como un objeto TypeScript tipado. El HTML solo se conecta a ese objeto, no lo define.
FormGroup y FormControl: el formulario como objeto tipadoUn FormGroup agrupa varios FormControl, uno por cada campo. El tipo genérico de
FormGroup<{ ... }> declara la forma completa del formulario, igual que una interface
declara la forma de un objeto:
artistaForm: FormGroup<{ nombre: FormControl<string>; paisOrigen: FormControl<string>;}> = new FormGroup({ nombre: new FormControl<string>('', { nonNullable: true, validators: [Validators.required], }), paisOrigen: new FormControl<string>('', { nonNullable: true, validators: [Validators.required], }),});Cada FormControl recibe dos cosas: un valor inicial ('') y un objeto de
opciones (nonNullable, validators).
nonNullable: true, por qué importaSin nonNullable: true, cada FormControl<string> sería en realidad
FormControl<string | null>: Angular permite que un control quede en null (por
ejemplo, al hacer .reset() sin un valor de reinicio explícito), y usted tendría que
verificar null cada vez que lea el valor.
Validators: validez y tipo son cosas distintasValidators.required marca el control como inválido mientras esté vacío, pero eso
no cambia su tipo: sigue siendo string, nunca undefined ni null. Validez
(“¿se puede enviar?”) y tipo (“¿qué forma tiene el valor?”) son dos preguntas distintas,
y Validators solo responde la primera.
| Validador | Qué exige |
|---|---|
Validators.required |
El control no puede quedar vacío. |
Validators.minLength(n) |
Al menos n caracteres. |
Validators.maxLength(n) |
A lo sumo n caracteres. |
Validators.email |
El texto tiene forma de correo electrónico. |
Validators.pattern(regex) |
El texto cumple una expresión regular. |
Un control puede recibir varios validadores a la vez, como un arreglo:
validators: [Validators.required, Validators.minLength(3)].
<form [formGroup]="artistaForm" (ngSubmit)="guardar()"> <input formControlName="nombre" /> <input formControlName="paisOrigen" />
<button type="submit" [disabled]="artistaForm.invalid"> Crear artista </button> <button type="button" (click)="cancelar()"> Cancelar </button></form>[formGroup]="artistaForm" conecta todo el <form> con el objeto artistaForm
del componente.formControlName="nombre" conecta un <input> puntual con el FormControl
correspondiente dentro del grupo.(ngSubmit)="guardar()" dispara guardar() cuando el formulario se envía, ya sea
con Enter o con un clic en un botón type="submit" dentro del <form>.[disabled]="artistaForm.invalid" deshabilita el botón mientras algún validador
falle.getRawValue() contra value()artistaForm.value existe, pero TypeScript lo tipa como Partial del objeto del
formulario (todas las propiedades opcionales), porque un control podría estar
deshabilitado y quedar fuera de value. artistaForm.getRawValue(), en cambio, siempre
devuelve todas las propiedades, con el tipo completo que declaró en el FormGroup.
guardar(): void { if (this.artistaForm.invalid) return;
const datos = this.artistaForm.getRawValue(); // datos: { nombre: string; paisOrigen: string } // tipado completo, sin Partial, gracias a nonNullable + getRawValue()}artistaForm.reset() devuelve cada control a su valor inicial — el que declaró al
crear el FormControl ('' en los ejemplos de arriba). Gracias a nonNullable: true,
ese valor inicial es siempre del tipo declarado, nunca null.
cancelar(): void { this.artistaForm.reset();}