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:
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:
La parte en la que me quiero concentrar es en el updateValueAndValidity() de los controles, es hermoso y explica mucho de su estructura:
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
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
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:
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.