Angular, Monkey-Patching y Change Detection

Todo buen framework frontend que se precie, debe tener un sistema de data binding que permita enlazar propiedades entre el controlador y la vista. Esto, que para los neófitos en materia front y quizás los no tanto puede llegar a ser pura magia negra creada en las profundidades del Monte del Destino, es uno de los pilares fundamentales en los que descansa Angular.

Si intentamos desmitificar este misterioso sistema, veremos que en Angular se habla de un proceso llamado Change Detection. La tarea básica de este proceso, es tomar el estado interno de nuestra aplicación, y reflejarlo de alguna manera en la interfaz de usuario.

Ahora bien, la pregunta del millón es: ¿cuándo Angular debe ejecutar este «costoso proceso» de comprobación? La respuesta es bien sencilla. Debe lanzarlo cada vez que el estado de la aplicación cambie.

Eddie Murphy sorprendido

Vale, quizás si ponemos esta respuesta en un examen de Angular nos caiga un cero… La pregunta correcta sería ¿cómo detecta Angular que el estado ha cambiado?

Debemos saber que básicamente el estado de una aplicación puede cambiar debido a tres cosas:

  • Events – clicksubmit, …
  • XHR – Recuperar datos de un servidor externo
  • Temporizadores – setTimeout()setInterval()

La naturaleza de todos estos elementos es asíncrona. Así pues, podríamos afirmar que siempre que haya una operación asíncrona, el estado de nuestra aplicación puede cambiar.

Monkey Pathching

Angular-Monkey-patching

Aquí es donde entra en juego el llamado Monkey-Patching. Este término es usado para explicar la manera que tiene un programa, en este caso nuestro framework, de extender o modificar, parte de un código en tiempo de ejecución. Típicamente en el Startup.

¿Qué quiero decir con esto? Pues que Angular tiene monkey-patched las funciones de naturaleza asíncrona. Los que provengan del mundo de AngularJS, se acordarán bien de aquellas funciones: $timeout, $interval, entre otras, que debían usarse en lugar de las nativas.

En las nuevas versiones de Angular, gracias al Monkey-Patching, podemos usar las funciones asíncronas nativas. Total transparencia para el desarrollador. Nada de $timeout’s, $interval’s y otras funciones extrañas. Debido al Monkey-Patching a parte del funcionamiento nativo normal, se lanzará el proceso de Change Detection.

Una vez cargada una app Angular, podemos intentar depurar, a modo de ejercicio, la función setTimeout. Si pulsamos F11 desde las devtools, veremos que la función nos es la nativa y está monkey-patched.

Explicar cómo están «trucadas» estas funciones asíncronas, queda fuera del alcance de este artículo. Comentar simplemente que se basa en las llamadas Zones. Podríamos decir que son algo parecido a un contexto de ejecución. Angular corre dentro de una de estas Zones, y gracias a ellas es capaz de resolver el problema de cuando comienzan y acaban ciertas tareas asíncronas.

Un poco de práctica

Rub-hands

Después de la tediosa teoría, me gustaría entrar en algo de práctica, que es lo que nos gusta a los developers. Y lo que en el fondo me impulsó a escribir este post.

Imaginemos que tenemos que controlar la creación y destrucción de un elemento html por medio de una media query de javascript. Para este menester la API nos ofrece un método llamado matchMedia.

Angular Ejemplo 1

Según este código la instancia de nuestro componente <h1> está ligada a la propiedad flag. Por defecto está inicializada a true, y cuando el navegador tenga un tamaño inferior a 1200px, el componente se destruirá.

Y esta es toda la teoría del código, pero si lo probamos, comprobaremos que no funciona.

Para probar los diferentes ejemplos debes abrir la preview en otra ventana clickando en el botón superior derecha de la pantalla. Encoge y agranda el tamaño del navegador.

Prueba el código aquí

¡Pues vaya con Angular, pronto empezamos a caerle mal! Tranquilidad, que no cunda el pánico, o sí… ¿Qué es lo que está pasando aquí? Pues lo que ocurre, es que no todos los métodos de la API de javascript que trabajan de forma asíncrona, están monkey-patched. Como es el caso de matchMedia y su addListener.

¿Y que podemos hacer ahora? Angular nos provee una utilidad que permite lanzar manualmente el Change Detector, comprobar si algo ha cambiado y refrescarlo en la vista.

Angular Ejemplo 2

Prueba el código aquí

¿Y si rizamos un poco el rizo?

Vamos a intentar algo sencillo, pero curioso. ¿Qué ocurriría si añadimos un botón dentro de nuestro componente con un evento de click? El siguiente código lo muestra.

Angular Ejemplo 3

Prueba el código aquí

  1. Haz click en el botón. La instancia desaparecerá.
  2. Encoge y agranda en navegador para que aparezca la instancia nuevamente.
  3. Prueba a hacer click en el botón otra vez.

¡¡¡Tachaaaan!!! ¡¡No funciona, esta roto!! Aquí es cuando algunos empiezan a odiar Angular y se cambian de framework ¿Pero qué demonios está pasando ahora?

Al agrandar el navegador se modifica el flag y se lanza manualmente el Change Detector. La instancia se crea, pero se crea fuera del contexto de Angular. Fuera de una NgZone. Por lo que la instancia y en este caso su evento click también se encuentran fuera del contexto.

Al hacer click en el botón, se ejecuta el método onClick correctamente. Pero Angular no es capaz de detectar que el callback ha terminado y, lanzar el proceso de detección de cambios. A la hora de añadir Angular el binding al evento, se podría decir que lo ha hecho con el método nativo y, no con el método monkey-patched.

Para solucionarlo podríamos hacerlo de la manera anterior a través del método this._cd.detectChanges(). Pero acabaríamos ensuciando nuestro bonito código con muchas llamadas de este tipo.

Una mejor solución, es decirle a Angular que ejecute dentro de su contexto, lo que está fuera de él, a través del método run.

Angular Ejemplo 4

Prueba el código aquí

Basta con envolver el código, hasta ahora ejecutado fuera del contexto de Angular, en una NgZone. La instancia del componente se creará dentro del contexto, incluidos sus eventos y bindings. Angular ahora será notificado gracias al Monkey-Patching, de cuando el callback termina su ejecución y, será capaz de lanzar el proceso de Change Detection automáticamente.

 Conclusión

Manejar lo básico de las nuevas versiones de Angular es fácil. Pero, reconozco que a un nivel más avanzado, la curva de aprendizaje se complica un poco. La buena noticia es que hemos intentado examinar algo de lo más complejo que tiene Angular. El proceso de Change Detection. Y yo espero haber resuelto un poco de ese misterio con este post.

<span style="font-size:80%">Autor </span><a href="https://blog.kairosds.com/author/eduardo-martin-picado/" target="_self">Eduardo Martin Picado</a>

Autor Eduardo Martin Picado

Ene 22, 2018

Otros artículos

Anotación @Lazy

Anotación @Lazy

¿Qué es? Es una anotación de Spring que nos permite posponer la creación de beans, de tal forma que éstos sólo se crearán cuando se vayan a utilizar, en lugar de crearlos al iniciar la aplicación. Ésto nos puede servir en aplicaciones que tienen funcionalidades muy...

Deceye – Turning transparency into depth

Deceye – Turning transparency into depth

¿DeFi? ¿DAOs? ¿En qué consiste eso? ¿Qué papel juegan? El ecosistema DeFi, Decentralized Finances, en castellano Finanzas Descentralizadas, engloba a todos aquellos servicios financieros que gracias a la tecnología blockchain pueden evitar la intermediación que ofrece...