Angular
Tutorial Angular: Formularios reactivos en Angular
Angular formularios reactivos: guía completa. Aprende a crear formularios reactivos en Angular con ejemplos prácticos y detallados.
Aprende Angular GRATIS y certifícateLos formularios reactivos en Angular, también conocidos como Reactive Forms
, son una forma de manejar formularios de manera más programática y declarativa en comparación con los formularios basados en plantillas (Template-driven Forms). Se trata de una técnica para desarrollar formularios donde la lógica de validación y gestión de los datos se realiza en el controlador en lugar de en la plantilla.
Los formularios reactivos se construyen utilizando FormControl
, FormGroup
y FormArray
a través del módulo ReactiveFormsModule
proporcionado por Angular. Estos formularios se definen completamente en el TypeScript del componente, permitiendo mayor flexibilidad y control sobre la validación, datos y estado del formulario.
Características principales
- Declaración en TypeScript: Los formularios se crean y manejan en los archivos TypeScript del componente. Esto facilita la aplicación de lógica compleja y la reutilización del código.
- Estructura inmutable: Cualquier cambio en el formulario produce una nueva instancia del mismo, lo que mejora la trazabilidad y el manejo de versiones.
- Validación a nivel de modelo: La validación de los datos se realiza con las APIs de Angular en el controlador, permitiendo definir reglas de validación de una manera más estructurada y robusta.
- Observables: Los cambios en los valores del formulario y de su estado se pueden observar utilizando
Observables
, lo que integra perfectamente con el sistema de reactividad de Angular basado enRxJS
.
Conceptos básicos
Los formularios reactivos en Angular están basados en tres clases principales: FormControl
, FormGroup
y FormArray
. Estas clases permiten construir y gestionar formularios de una manera declarativa, facilitando la validación y la manipulación dinámica de los formularios.
FormControl
FormControl
representa un único campo de entrada en el formulario. Se utiliza para rastrear el valor, el estado y la validación de un control de formulario individual. Un FormControl
se puede instanciar con un valor inicial, un array de validadores síncronos y un array de validadores asíncronos.
Ejemplo:
import { FormControl } from '@angular/forms';
const nameControl = new FormControl('initial value', [Validators.required, Validators.minLength(3)]);
FormGroup
FormGroup
es una colección de FormControl
u otros FormGroup
, permitiendo anidar conjuntos de controles en estructuras jerárquicas. Es útil para representar formularios complejos y facilita la agrupación y validación conjunta de varios controles.
Ejemplo:
import { FormGroup, FormControl, Validators } from '@angular/forms';
const userProfileForm = new FormGroup({
firstName: new FormControl('', [Validators.required]),
lastName: new FormControl(''),
email: new FormControl('', [Validators.email]),
});
FormArray
FormArray
es una colección ordenada de FormControl
, FormGroup
o incluso otros FormArray
. Este componente es útil cuando se debe manejar un conjunto dinámico de controles, como una lista de elementos con posibilidad de agregar o quitar elementos de forma dinámica.
Ejemplo:
import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';
const addressFormArray = new FormArray([
new FormGroup({
street: new FormControl('', Validators.required),
city: new FormControl('', Validators.required)
})
]);
addressFormArray.push(new FormGroup({
street: new FormControl(''),
city: new FormControl('')
}));
FormBuilder
FormBuilder
es un servicio proporcionado por Angular que simplifica la creación de formularios reactivos complejos. Este servicio ofrece métodos convenientes para crear instancias de FormControl
, FormGroup
y FormArray
, lo que resulta en un código más limpio y fácil de leer, especialmente para formularios grandes.
Para utilizar FormBuilder
, primero debes inyectarlo en el constructor de tu componente:
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-profile-form',
templateUrl: './profile-form.component.html'
})
export class ProfileFormComponent {
profileForm: FormGroup;
constructor(private fb: FormBuilder) {
this.createForm();
}
createForm() {
this.profileForm = this.fb.group({
firstName: ['', Validators.required],
lastName: [''],
email: ['', [Validators.required, Validators.email]],
address: this.fb.group({
street: [''],
city: [''],
state: [''],
zip: ['']
}),
aliases: this.fb.array([
this.fb.control('')
])
});
}
}
En este ejemplo, fb.group()
crea un FormGroup
, fb.control()
crea un FormControl
, y fb.array()
crea un FormArray
.
FormBuilder
es especialmente útil cuando trabajas con formularios grandes o complejos, ya que reduce significativamente la cantidad de código repetitivo y mejora la legibilidad.
Ejemplo de cómo añadir dinámicamente controles a un FormArray
usando FormBuilder
:
addAlias() {
const aliases = this.profileForm.get('aliases') as FormArray;
aliases.push(this.fb.control(''));
}
En este caso, fb.control('')
crea un nuevo FormControl
y lo añade al FormArray
de aliases.
Manejo del estado y valores
Cada uno de estos componentes (FormControl
, FormGroup
, FormArray
) tiene propiedades y métodos similares para manejar sus estados y valores. Las propiedades más comunes incluyen:
- value: El valor actual del control o grupo.
- status: El estado de validación actual,
VALID
,INVALID
,PENDING
oDISABLED
. - errors: Los errores actuales si el control es inválido.
- touched: Indicador de si el control ha sido tocado por el usuario.
- untouched: Indicador de si el control no ha sido tocado por el usuario.
- pristine: Indicador de si el control no ha sido modificado.
- dirty: Indicador de si el control ha sido modificado.
Métodos comunes incluyen:
- setValue: Establece un nuevo valor para el control, grupo o array.
- patchValue: Similar a
setValue
, pero permite establecer valores de forma parcial. - reset: Reinicia el control al estado inicial.
- disable: Desactiva el control.
- enable: Activa el control.
Ejemplo:
// Acceder y modificar el valor de un control
userProfileForm.get('firstName')?.setValue('John');
// Comprobar el estado
if (userProfileForm.valid) {
// Procesar los datos del formulario
}
// Resetear el formulario
userProfileForm.reset();
Validadores
En Angular, la validación de formularios reactivos es esencial para asegurar la integridad de los datos ingresados por los usuarios. Los formularios reactivos proporcionan una manera flexible y poderosa de definir y manejar las reglas de validación directamente en el componente. A continuación, se detalla cómo implementar validaciones en formularios reactivos.
Validadores predefinidos
Angular ofrece una serie de validadores integrados que puedes utilizar para reglas de validación comunes. Estos validadores están disponibles a través del módulo Validators
.
Ejemplos de validadores integrados:
Validators.required
: Verifica que el campo no esté vacío.Validators.email
: Valida que el valor sea un email en formato correcto.Validators.minLength(4)
: Verifica que el campo tenga al menos 4 caracteres.Validators.maxLength(10)
: Verifica que el campo no tenga más de 10 caracteres.Validators.pattern('^[a-zA-Z]+$')
: Valida que el valor siga un patrón específico.
Cómo usar validadores integrados:
import { FormControl, Validators } from '@angular/forms';
const nameControl = new FormControl('', [Validators.required, Validators.minLength(4)]);
const emailControl = new FormControl('', [Validators.required, Validators.email]);
const passwordControl = new FormControl('', [Validators.required, Validators.pattern('^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$')]);
Validadores personalizados
A veces, los validadores integrados no son suficientes para las necesidades específicas de una aplicación. En estos casos, se pueden construir validadores personalizados.
Un validador personalizado es una función que recibe un AbstractControl
y devuelve un objeto de errores o null
si la validación es exitosa.
Ejemplo de validador personalizado para un nombre prohibido:
import { AbstractControl, ValidationErrors } from '@angular/forms';
function forbiddenNameValidator(control: AbstractControl): ValidationErrors | null {
const forbidden = /forbiddenName/.test(control.value);
return forbidden ? { forbiddenName: { value: control.value } } : null;
}
const usernameControl = new FormControl('', [forbiddenNameValidator]);
Validadores asíncronos
Los validadores asíncronos se utilizan cuando la validación depende de una operación asíncrona, como una llamada a un servidor. Un validador asíncrono devuelve un Observable que resuelve los errores o null
.
Ejemplo de validador asíncrono para verificar la disponibilidad del nombre de usuario:
import { FormControl, AsyncValidatorFn } from '@angular/forms';
import { Observable, of, map, delay } from 'rxjs';
usernameTakenValidator(): AsyncValidatorFn {
return (control: AbstractControl): Observable<ValidationErrors | null> => {
return of(control.value === 'taken').pipe(
delay(1000),
map(isTaken => (isTaken ? { usernameTaken: true } : null))
);
};
}
const asyncUsernameControl = new FormControl('', [], [usernameTakenValidator]);
Mensajes de error
Para proporcionar feedback útil al usuario, se pueden mostrar mensajes de error en la plantilla HTML. Usando las nuevas estructuras de control @if
, se pueden mostrar mensajes específicos para cada tipo de error.
Ejemplo de muestra de mensajes de error:
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
<label for="name">Nombre:</label>
<input id="name" formControlName="name">
@if (nameControl?.invalid && nameControl?.touched) {
@if (nameControl?.hasError('required')) {
<small>El nombre es obligatorio.</small>
}
@if (nameControl?.hasError('minlength')) {
<small>El nombre debe tener al menos 4 caracteres.</small>
}
}
<label for="email">Email:</label>
<input id="email" formControlName="email">
@if (emailControl?.invalid && emailControl?.touched) {
@if (emailControl?.hasError('required')) {
<small>El email es obligatorio.</small>
}
@if (emailControl?.hasError('email')) {
<small>Introduce un email válido.</small>
}
}
<button type="submit" [disabled]="registerForm.invalid">Registrar</button>
</form>
Este sería el componente Typescript:
import { CommonModule } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
@Component({
selector: 'app-register-form',
standalone: true,
imports: [FormsModule, CommonModule, ReactiveFormsModule],
templateUrl: './register-form.component.html',
styleUrls: ['./register-form.component.css']
})
export class RegisterFormComponent implements OnInit {
registerForm!: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.registerForm = this.fb.group({
name: ['', [Validators.required, Validators.minLength(4)]],
email: ['', [Validators.required, Validators.email]]
});
}
onSubmit() {
if (this.registerForm.valid) {
console.log('Formulario enviado', this.registerForm.value);
// Aquí iría la lógica para enviar los datos al servidor
} else {
console.log('Formulario inválido');
// Marcar todos los campos como tocados para mostrar los errores
Object.values(this.registerForm.controls).forEach(control => {
control.markAsTouched();
});
}
}
// Métodos de utilidad para acceder a los controles del formulario desde la plantilla
get nameControl() {
return this.registerForm.get('name');
}
get emailControl() {
return this.registerForm.get('email');
}
}
Así se vería este ejemplo en el navegador, sin hacer cambios en los estilos:
Manejadores de validación en FormGroup
Puedes también definir validaciones a nivel de FormGroup
para validaciones cruzadas entre múltiples controles.
Ejemplo de validador de coincidencia de contraseñas:
import { FormGroup, ValidationErrors, ValidatorFn } from '@angular/forms';
const passwordMatchValidator: ValidatorFn = (group: FormGroup): ValidationErrors | null => {
const password = group.get('password');
const confirmPassword = group.get('confirmPassword');
return password && confirmPassword && password.value !== confirmPassword.value ? { passwordMismatch: true } : null;
};
this.registerForm = new FormGroup({
password: new FormControl(''),
confirmPassword: new FormControl(''),
}, { validators: passwordMatchValidator });
Este enfoque asegura que tanto controladores individuales como grupos de control puedan ser validados de manera eficiente siguiendo las necesidades específicas del negocio.
Pasos para crear un formulario reactivo
1.- Importar ReactiveFormsModule
Asegúrate de importar el módulo ReactiveFormsModule
de @angular/forms
en el módulo principal (app.module.ts
).
// app.module.ts
import { ReactiveFormsModule } from '@angular/forms';
// ...
@NgModule({
imports: [
BrowserModule,
ReactiveFormsModule // Añadir ReactiveFormsModule aquí
],
// ...
})
export class AppModule { }
En caso de estar en un componente standalone true entonces importarlo directamente sobre el componente:
2.- Crear un componente para el formulario
Genera un nuevo componente para tu formulario llamado mi-formulario-reactivo
utilizando Angular CLI.
ng generate component mi-formulario-reactivo
3.- Importar las clases necesarias en el componente
En tu componente (mi-formulario-reactivo.component.ts
), importa FormGroup
y FormControl
desde @angular/forms
.
import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
@Component({
selector: 'app-mi-formulario-reactivo',
templateUrl: './mi-formulario-reactivo.component.html',
styleUrls: ['./mi-formulario-reactivo.component.css']
})
export class MiFormularioReactivoComponent {
// ...
}
4.- Definir la estructura del formulario en el componente
Utiliza instancias de FormGroup
y FormControl
para definir la estructura del formulario. Este modelo de formulario se vinculará más adelante con la plantilla HTML.
export class MiFormularioReactivoComponent {
registerForm = new FormGroup({
name: new FormControl(''),
email: new FormControl(''),
});
onSubmit() {
console.log(this.registerForm.value);
}
}
5.- Vincular los elementos del formulario a las instancias de las clases en la plantilla del componente
En la plantilla de tu componente (mi-formulario-reactivo.component.html
), utiliza el atributo formGroup
para vincular el formulario y formControlName
para vincular cada control de formulario.
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
<label for="name">Nombre:</label>
<input id="name" formControlName="name">
<label for="email">Email:</label>
<input id="email" formControlName="email">
<button type="submit">Registrar</button>
</form>
Ejemplo:
6.- Manejar el envío del formulario
Implementa la función onSubmit()
en tu componente para manejar el evento de envío del formulario. Aquí puedes, por ejemplo, enviar los datos a un servidor o validarlos antes de la transmisión.
export class MiFormularioReactivoComponent {
// ...
onSubmit() {
if (this.registerForm.valid) {
console.log(this.registerForm.value);
}
}
}
Ejemplo de función save
que captura los datos del formulario a un objeto Book
y lo envía a backend:
Observar cambios
En los formularios reactivos de Angular, el observar cambios en los valores y estados de los controles es fundamental para crear aplicaciones dinámicas y reactivas. Esto se realiza mediante el uso de observables que son emitidos por los controles de formulario (FormControl
), grupos de formulario (FormGroup
) y arrays de formulario (FormArray
). Angular, al estar profundamente integrado con RxJS
, ofrece una API sencilla para ello.
this.formulario.valueChanges.subscribe(cambios => {
console.log(cambios);
});
Observables de cambio de valor
Cada instancia de FormControl
, FormGroup
y FormArray
proporciona un observable de su valor (valueChanges
) que se puede suscribir para recibir notificaciones cada vez que el valor cambia.
Ejemplo de uso de valueChanges
en un FormControl
:
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-login-form',
templateUrl: './login-form.component.html'
})
export class LoginFormComponent {
username = new FormControl('');
constructor() {
this.username.valueChanges.subscribe(value => {
console.log('Username changed to:', value);
});
}
}
Observables de cambio de estado
Los controles de formulario también proporcionan un observable para los cambios en su estado (statusChanges
). Este observable es útil para monitorizar el estado de validación del control.
Ejemplo de uso de statusChanges
en un FormGroup
:
import { Component } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
@Component({
selector: 'app-auth-form',
templateUrl: './auth-form.component.html'
})
export class AuthFormComponent {
authForm = new FormGroup({
email: new FormControl('', [Validators.required, Validators.email]),
password: new FormControl('', [Validators.required, Validators.minLength(6)])
});
constructor() {
this.authForm.statusChanges.subscribe(status => {
console.log('Form status changed to:', status);
});
}
}
Observación conjunta de valores o estados
Para observar los cambios en múltiples controles de formulario, se puede utilizar combinación de observables (combineLatest
, merge
, etc.) proporcionada por RxJS
.
Ejemplo de observación conjunta de valores:
import { Component } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { combineLatest } from 'rxjs';
@Component({
selector: 'app-multi-field-form',
templateUrl: './multi-field-form.component.html'
})
export class MultiFieldFormComponent {
multiFieldForm = new FormGroup({
field1: new FormControl('', Validators.required),
field2: new FormControl('', Validators.required)
});
constructor() {
const field1$ = this.multiFieldForm.get('field1').valueChanges;
const field2$ = this.multiFieldForm.get('field2').valueChanges;
combineLatest([field1$, field2$]).subscribe(([field1Value, field2Value]) => {
console.log('Field1:', field1Value, ' Field2:', field2Value);
});
}
}
Utilización práctica en formularios avanzados
Observar cambios es especialmente útil para casos más avanzados como formularios dependientes dónde, por ejemplo, el valor de un campo depende del valor de otro. Este patrón es comúnmente usado para mostrar mensajes de error en tiempo real, habilitar/deshabilitar botones de formulario, o modificar la UI dinámicamente.
Ejemplo de uso práctico en un formulario dependiente:
import { Component } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
@Component({
selector: 'app-dependent-form',
templateUrl: './dependent-form.component.html'
})
export class DependentFormComponent {
dependentForm = new FormGroup({
country: new FormControl(''),
state: new FormControl({ value: '', disabled: true })
});
constructor() {
this.dependentForm.get('country').valueChanges.subscribe(selectedCountry => {
const stateControl = this.dependentForm.get('state');
if (selectedCountry === 'USA') {
stateControl.enable();
stateControl.setValidators(Validators.required);
} else {
stateControl.disable();
stateControl.clearValidators();
}
stateControl.updateValueAndValidity();
});
}
}
Este enfoque proporciona una experiencia de usuario enriquecida y mejora la interactividad de la aplicación utilizando las capacidades reactivas de Angular y RxJS
.
Ejercicios de esta lección Formularios reactivos en Angular
Evalúa tus conocimientos de esta lección Formularios reactivos en Angular con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Signals en Angular
Guards funcionales
Decodificar JWT en Angular
Servicio con HttpClient
Ciclo de vida de componentes en Angular
Gestión de productos de Fake Store API
Data binding en Angular
Routes sin módulos en Angular
Router en Angular
Instalación de Angular
Route Guards basados en interfaces
La directiva @if en Angular
Formularios reactivos en Angular
Servicios en Angular
Interceptor funcional
Servicio con Array
La directiva @for en Angular
Interceptores HTTP
Componentes standalone true
Formularios con ngModel en Angular
Routes en Angular
Comunicación entre componentes Angular
Parámetros en rutas con ActivatedRoute
CRUD de Restaurantes y Platos
Tablas en Angular Material
Formulario de registro de usuarios
Instalación y uso de NgBoostrap
Desarrollo de componentes Angular
JWT en Angular
Formularios reactivos en Angular
Formularios en Angular Material
Layout con Angular Material
Effects en Angular
Data binding
HttpClient en servicios de Angular
Desarrollo de módulos Angular
Comandos Angular CLI
Subir archivo en formularios
La directiva routerLink en Angular
Todas las lecciones de Angular
Accede a todas las lecciones de Angular y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Instalación Angular
Introducción Y Entorno
Comandos Angular Cli
Introducción Y Entorno
Desarrollo De Componentes Angular
Componentes
Data Binding En Angular
Componentes
Ciclo De Vida De Componentes En Angular
Componentes
Comunicación Entre Componentes Angular
Componentes
La Directiva @If En Angular
Componentes
La Directiva @For En Angular
Componentes
Componentes Standalone
Componentes
Desarrollo De Módulos Angular
Módulos
Routes En Angular
Enrutado Y Navegación
Router En Angular
Enrutado Y Navegación
La Directiva Routerlink En Angular
Enrutado Y Navegación
Parámetros En Rutas Con Activatedroute
Enrutado Y Navegación
Routes Sin Módulos En Angular
Enrutado Y Navegación
Servicios En Angular
Servicios E Inyección De Dependencias
Httpclient En Servicios De Angular
Servicios E Inyección De Dependencias
Formularios Con Ngmodel En Angular
Formularios
Formularios Reactivos En Angular
Formularios
Subir Archivo En Formularios
Formularios
Layout Con Angular Material
Integración Con Angular Material
Tablas En Angular Material
Integración Con Angular Material
Formularios En Angular Material
Integración Con Angular Material
Instalación Y Uso De Ngboostrap
Integración Con Bootstrap Css
Signals En Angular
Signals Y Reactividad
Effects En Angular
Signals Y Reactividad
Route Guards Basados En Interfaces
Autenticación Y Autorización
Guards Funcionales
Autenticación Y Autorización
Interceptores Http Basados En Interfaz
Autenticación Y Autorización
Interceptores Http Funcionales
Autenticación Y Autorización
Seguridad Jwt En Angular
Autenticación Y Autorización
Decodificar Tokens Jwt En Angular
Autenticación Y Autorización
Certificados de superación de Angular
Supera todos los ejercicios de programación del curso de Angular y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender el concepto de formularios reactivos en Angular.
- Crear y gestionar formularios usando
FormControl
,FormGroup
yFormArray
. - Implementar validaciones síncronas y asíncronas.
- Utilizar observables para rastrear y reaccionar a cambios en los formularios.
- Aprovechar
FormBuilder
para simplificar la construcción de formularios complejos. - Manejar el estado y los valores del formulario de manera eficiente.