El detrás de escenas de los formularios en Angular

Presentó la estructura que maneja Angular para abstraer los controles.

hace 3 años   •   5 min de lectura

Por Andrés Tuñón
El verdadero culpable es el Abstract Control
Tabla de contenidos

Uno de los puntos fuertes de Angular son los Reactive Forms y su forma de abstraer los controles. Voy a hacer una introducción rápida para el que no lo conoce:

Abstracción de los controles con Reactive Form
Abstracción de los controles

FormControl: Es la abstracción de un control. Por ejemplo un input, un select e incluso componentes que no necesariamente deben ser controles.

FormGroup: Es el encargado de agrupar los FormControls.

Por ejemplo, tengo que registrar el nombre y apellido en un formulario, sería algo como lo siguiente: (Puedes dar click para agrandar la imagen)

Cada vez que escribes en el campo, la clase FormControl recibe el valor;  de igual forma al darle un valor al FormControl, en el campo se ve reflejado (2 ways binding de Angular).

Estas clases tienen propiedades y funciones (creadas por el equipo de Angular) que facilitan mucho el realizar validaciones:

Status

Ya sea un FormControl o un FormGroup, tienen un status. Estos status pueden ser:

  • VALID: Cuando el control pasa todas las validaciones.
  • INVALID: Cuando el control o alguno de sus controles internos no pasó alguna de las validaciones.
  • PENDING: Cuando se espera que se resuelva una validación asíncrona.
  • PRISTINE: Cuando aún no se ha modificado el control.
  • DIRTY: Cuando ya se ha modificado el control.
  • UNTOUCHED: Cuando el campo aún no fue tocado.
  • TOUCHED: Cuando el campo ya fue tocado.

Valid y su utilidad

Para validar todo el FormGroup simplemente se debe consultar la propiedad valid (que revisa si el status es 'VALID'). En el ejemplo del formulario completo, solo es necesario que 'lastname' sea inválido para que todo el FormGroup sea inválido.

markAllAsTouched()

Supongamos que el formulario no es válido, el siguiente paso será darle al usuario retroalimentación de lo que debe corregir.

Esta es la razón de markAllAsTouched: al marcar como 'TOUCHED' los controles inválidos, se puede mostrar los mensajes de validación sobre los campos que no son válidos y el usuario hizo el intento de escribir en ellos.

¿Por qué debe ser touched y no dirty?

Realmente depende de la necesidad y tu lógica, es una especie de convención. Usualmente, se utiliza touched y no dirty; ya que es la primera interacción registrada sobre el control.

'Dirty' registra cuando se modificó el valor, mientras que 'touched' es cuando simplemente fue tocado; es decir, un campo puede ser pristine y touched.

Validators

Puede que en este punto te hayas preguntado ¿cómo es que validas realmente un campo?, aquí entra en juego los Validators que provee Angular y los que igual puedes crear tú.

Digamos que quiero validar el nombre en un formulario:

Validators.required es el responsable de darle el status de 'INVALID' al formulario completo.

Después de realizar una muy breve introducción, voy a la razón de este artículo:

Lo hermosa estructura del AbstractControl

Ya sea FormControl, FormGroup o FormArray, los tres son AbstractControl y a continuación te muestro su código:

angular/model.ts at 8bf3b53a1d6d04de85545807d4eebbb3480343ae · angular/angular
The modern web developer’s platform. Contribute to angular/angular development by creating an account on GitHub.
Código de los Abstract Controls

La parte en la que me quiero concentrar es en el updateValueAndValidity() de los controles, es hermoso y explica mucho de su estructura:

updateValueAndValidity()
updateValueAndValidity()

setInitialStatus

setInitialStatus
setInitialStatus

Esta función simplemente reasigna o inicializa el estado del control como deshabilitado o válido, es una especie de flag.

updateValue

Esta función tiene 3 diversos comportamientos dependiendo de la Abstracción:

  • FormControl: no hace nada, ya tiene el valor
  • FormGroup: se obtiene el valor de los controles para armar el objeto y asignarlo como valor
  • FormArray: se obtiene el valor de los controles para armar el arreglo y asignarlo como valor

runValidator y calculateStatus

  • Corre las validaciones con runValidator() y por dentro ejecuta todas las validaciones que le agregues al control.
  • Verifica los estados:
  • En caso de tener errores, retorna inválido.
  • Si alguno de los controles es inválido, todo el formulario es invalido.
  • Si pasa todas estas revisiones, retorna válido.

emitEvent

emitEvent
emitEvent

Dentro de las opciones de los controles hay una opción para evitar emitir que los valores y los estados fueron cambiados.  Esto es relevante cuando te suscribes a los controles para alguna lógica específica.

onlySelf

onlySelf
onlySelf

Esta es otra opción de los controles, que solo se actualice el valor y validaciones del control actual. Por defecto ejecuta toda esta misma función; pero en los padres, esta es la razón por la que ajusta las validaciones en cadena hasta llegar a la raíz, ¡es una gran estructura!

A continuación el link a la línea en específico:

angular/model.ts at 8bf3b53a1d6d04de85545807d4eebbb3480343ae · angular/angular
The modern web developer’s platform. Contribute to angular/angular development by creating an account on GitHub.
Código de updateValueAndValidity

Conclusión

En general, ahora deberías tener una mejor claridad de cómo funciona por dentro el modelado de los controles de Angular.

Gracias a esta investigación pude heredar de la clase AbstractControl para crear mis controles, validadores y funciones replicando la misma lógica.

Corre la voz

Sigue leyendo