El framework Angular proporciona dos enfoques para manejar formularios: los controlados por template y los reactivos. Ninguno de los enfoques se considera mejor; se debe elegir el que mejor se adapte a su escenario.
La principal diferencia entre los dos enfoques es cómo gestionan los datos:
- Formularios controlados por
template: Son fáciles de configurar y añadir a una aplicación Angular. Operan únicamente en eltemplatedel componente para crear elementos y configurar reglas de validación; por lo tanto, no son fáciles de probar. También dependen del mecanismo de detección de cambios delframework. - Formularios reactivos: Son más son más fáciles de escalar y probar. Operan en la clase del componente para gestionar los controles de entrada y establecer las reglas de validación. También manipulan los datos utilizando un modelo de formulario intermedio, manteniendo su naturaleza inmutable.
En los formularios controlados por template, se pueden usar two-way binding para leer y escribir datos simultáneamente. Los formularios controlados por template proporcionan la directiva ngModel, que se puede usar en los componentes para obtener este comportamiento. Para obtener más información sobre los formularios controlados por template, se convertirá la funcionalidad de cambio de precio del componente de detalle del producto para que funcione con formularios Angular.
Mientras se está escribiendo en el input, la directiva ngModel actualiza el valor del precio del producto.
El nuevo precio se refleja directamente en el template porque se utiliza la sintaxis de interpolación de Angular para mostrar su valor.
En este caso, actualizar el precio del producto mientras se introduce uno nuevo es una mala experiencia de usuario.
El usuario debería poder ver el precio actual del producto en todo momento. Se modifica el
componente de detalle del producto para que el precio se muestre correctamente. Esto se consigue haciendo el binding a una variable auxiliar en lugar de usar directamente el producto.
Los reactive forms, como su nombre indica, proporcionan acceso de forma reactiva a los formularios web. Están construidos pensando en la reactividad, donde los controles de input y sus valores pueden manipularse utilizando observable streams. También mantienen un estado inmutable de los datos del formulario, lo que los hace más fáciles de probar porque se puede estar seguro de que el estado del formulario puede modificarse de forma explícita y consistente. Los reactive forms tienen un enfoque programático para crear elementos de formulario y establecer reglas de validación configurando todo en la clase del componente. Las clases clave de Angular involucradas en este enfoque son las siguientes:
FormControl: Representa un control de formulario individual, como un elemento<input>.FormGroup: Representa una colección de controles de formulario. El elemento<form>es elFormGroupsuperior en la jerarquía de unreactive form.FormArray: Representa una colección de controles de formulario, al igual queFormGroup, pero puede modificarse en tiempo de ejecución. Por ejemplo, se pueden añadir o eliminar objetosFormControldinámicamente según sea necesario.FormBuilder: Proporciona una forma más sencilla de crear instancias deFormControl,FormGroupyFormArray. Permite crear formularios de forma más concisa y legible.
Las clases anteriores están disponibles en el paquete npm @angular/forms y contienen propiedades que pueden utilizarse en los siguientes escenarios:
- Para renderizar la UI de forma diferente según el estado de un formulario o control.
- Para comprobar si se ha interactuado con un formulario o control. Se explorará cada clase de formulario a través de un ejemplo en nuestra aplicación Angular.
En el primer ejemplo, se introducen los reactive forms en el componente de creación de productos.
Considere el escenario en el que se han añadido algunos productos al carrito de compras de nuestra aplicación de comercio electrónico y se desea actualizar sus cantidades antes de finalizar el pedido. Actualmente, nuestra aplicación no tiene ninguna funcionalidad para un carrito de compras, por lo que ahora se añadirá una:
ng generate interface cart
ng generate service cartEl uso de clases de formulario para construir formularios Angular puede volverse repetitivo y tedioso para escenarios complejos. El framework Angular proporciona FormBuilder, un servicio incorporado a los formularios de Angular que contiene métodos de ayuda para construir formularios. Aquí se usa para construir un formulario para crear nuevos productos.
Al usar el servicio FormBuilder para crear formularios Angular, no es necesario tratar explícitamente con los tipos de datos FormGroup y FormControl, aunque eso es lo que se está creando internamente.
De todas formas, la aplicación se va a construir sin usar FormBuilder.
Un formulario Angular debe validar el input y proporcionar retroalimentación visual para mejorar la UX y guiar a los usuarios a completar el formulario con éxito. Se investigarán las siguientes formas de validar formularios en aplicaciones Angular:
- Validación global con CSS
- Validación en la clase del componente
- Validación en el
templatedel componente - Construcción de validadores personalizados
El framework Angular establece automáticamente las siguientes clases CSS en un formulario, basado en template o reactivo, que se pueden usar para proporcionar retroalimentación al usuario:
ng-untouched: Indica que aún no se ha interactuado con un formulario.ng-touched: Indica que se ha interactuado con un formulario.ng-dirty: Indica que se ha asignado un valor a un formulario.ng-pristine: Indica que aún no se ha modificado un formulario.
Además, Angular añade las siguientes clases en el elemento HTML de un control de formulario:
ng-valid: Indica que el valor de un formulario es válido.ng-invalid: Indica que el valor de un formulario no es válido.
Angular establece las clases CSS anteriores en el formulario y sus controles según su estado. El estado del formulario se evalúa según el estado de sus controles. Por ejemplo, si al menos un control de formulario no es válido, Angular establecerá la clase CSS ng-invalid en el formulario y el control correspondiente.
En la sección anterior, se aprendió que Angular añade una colección de clases CSS predefinida al validar formularios Angular. Cada clase tiene una propiedad booleana correspondiente en el modelo de formulario respectivo, tanto en formularios basados en template como en formularios reactivos:
untouched: Indica que aún no se ha interactuado con un formulario.touched: Indica que se ha interactuado con un formulario.dirty: Indica que se ha asignado un valor a un formulario.pristine: Indica que aún no se ha modificado un formulario.valid: Indica que el valor de un formulario es válido.invalid: Indica que el valor de un formulario no es válido.
Los formularios basados en template dependen únicamente del template del componente para realizar las validaciones. En los formularios reactivos, la fuente de la verdad es el modelo de formulario que reside en la clase TypeScript del componente. Se definen las reglas de validación en formularios reactivos al construir la instancia de FormGroup mediante programación.
Cuando se añade un validador usando la clase FormControl, se puede eliminar el atributo HTML respectivo del template HTML. Sin embargo, se recomienda mantenerlo por razones de accesibilidad, para que las aplicaciones de lectura de pantalla puedan usarlo.
Los validadores predefinidos no cubren todos los escenarios que se puedan encontrar en una aplicación Angular; sin embargo, escribir un validador personalizado y usarlo en un formulario Angular es sencillo. En este caso, se construye un validador para comprobar que el precio de un producto no puede exceder un umbral especificado.
Se podría usar el validador max incorporado para realizar la misma tarea. Sin embargo, se usa la función validadora con fines de aprendizaje.
Los validadores personalizados se utilizan cuando se desea validar un formulario o un control con código personalizado. Por ejemplo, para comunicarse con una API para validar un valor, o para realizar un cálculo complejo para validar un valor.
En este ejemplo se crea un validador personalizado para formularios reactivos. También se podría hacer un validador personalizado para templates, pero, en ese caso, habría que haber creado una directiva.
Finalmente, Angular soporta validadores asíncronos. Consultar aquí para más información.
El estado de un formulario Angular difiere entre los formularios controlados
por template y los formularios reactivos. En los primeros, el estado es un
objeto simple, mientras que en los últimos, se mantiene en el modelo del
formulario. En esta sección, aprenderemos sobre los siguientes conceptos:
- Actualización del estado del formulario
- Reacción a los cambios de estado
Trabajar con el estado del formulario en formularios controlados por template
es relativamente fácil. Se debe interactuar con la propiedad del componente
vinculada a la directiva ngModel de un control de formulario.
En los formularios reactivos, se puede usar la propiedad value de una
instancia de FormControl o los siguientes métodos de la clase FormGroup
para cambiar valores en todo el formulario:
setValue: Reemplaza valores en todos los controles del formulario.patchValue: Actualiza valores en controles específicos del formulario.
El método setValue acepta un objeto como parámetro que contiene pares
clave-valor para todos los controles del formulario. Si se desea completar
los detalles de un producto en el componente de creación de producto de forma
programática, el siguiente fragmento sirve como ejemplo:
this.productForm.setValue({
title: 'TV monitor',
price: 600,
category: 'electronics'
});En el fragmento anterior, cada clave del objeto pasado en el método setValue
debe coincidir con el nombre de cada control de formulario. Si se omite uno,
Angular arrojará un error.
Si se desea completar algunos de los detalles de un producto, se puede usar
el método patchValue:
this.productForm.patchValue({
title: 'TV monitor',
category: 'electronics'
});Un escenario común al trabajar con formularios Angular es que se desea desencadenar un efecto secundario cuando el valor de un control de formulario cambia. Un efecto secundario puede ser cualquiera de los siguientes:
- Alterar el valor de un control de formulario.
- Iniciar una solicitud HTTP para filtrar el valor de un control de formulario.
- Habilitar/deshabilitar ciertas partes del
templatedel componente.
En los formularios controlados por template, se puede usar una versión
extendida de la directiva ngModel para recibir notificaciones cuando su
valor cambia. La directiva ngModel contiene las siguientes propiedades
vinculables:
ngModel: Una propiedad deinputpara pasar valores al control.ngModelChange: Una propiedad deoutputpara recibir notificaciones cuando el valor del control cambia.
Se puede escribir el binding de ngModel en el elemento HTML <input>
del componente de detalle del producto de la siguiente manera alternativa:
<input
placeholder="New price"
type="number"
name="price"
required min="1"
appPriceMaximum threshold="500"
#priceCtrl="ngModel"
[ngModel]="price"
(ngModelChange)="price = $event" />En el fragmento anterior, se establece el valor de la propiedad de input
ngModel usando property binding y el valor de la propiedad del componente
price usando event binding. Angular dispara el evento ngModelChange
automáticamente e incluye el nuevo valor del elemento HTML <input> en la
propiedad $event. Se puede usar el evento ngModelChange para cualquier
efecto secundario en el componente cuando el valor del control de formulario
price cambia.
En los formularios reactivos, se utiliza una API basada en observables para
reaccionar a los cambios de estado. Las clases FormGroup y FormControl
contienen el observable valueChanges, el cual se puede usar para
suscribirse y recibir notificaciones cuando el valor del formulario o del
control cambia.
Esto se usa para restablecer el valor del control de formulario price en el
componente de creación de producto cuando la categoría cambie.