Reduciendo la complejidad de los controladores en Angular
Otro de los grandes problemas que encontramos en una aplicación Angular en la que trabajamos el año pasado fue entender y modificar controladores inmensos con varios cientos o incluso miles de líneas de código. Estos controladores estaban asumiendo demasiadas reponsabilidades y contenían un montón de código procedural, lo que los hacía muy difíciles de entender, modificar y testear. En la nueva aplicación Angular en la que estamos trabajando actualmente, estamos refactorizando de manera muy agresiva para mantener bajo control el tamaño y la complejidad de sus controladores. Aparte de la estrategía para prevenir la obsesión por primitivas en los accesos a datos de los resources que comentamos en un post anterior, otra técnica que nos está dando muy buenos resultados es extraer tanta lógica y estado como nos es posible de los controladores, y moverla a pequeños objetos JavaScript que llamamos widgets. Esta simple técnica nos ha ayudado a reducir el tamaño y la complejidad de los controladores y, además, ha mejorado notablemente la testeabilidad del código. A continuación, mostramos los resultados de uno de estos refactorings para que puedan ver el antes y el después de uno de nuestros controladores. Esta es la primera versión de un controlador que está coordinando el conportamiento y contenido de una página que muestra los detalles de los datos registrados por un GPS durante una actividad deportiva. La página contiene un mapa (que usa OpenLayers) sobre el que se muestra la polilínea del recorrido registrado, varias estadísticas de los datos registrados y varios gráficos (creados con la librería flot)) que representan algunos de los datos registrados. Además es posible interactuar con estos gráficos cambiando la magnitud representada en su eje x (que puede ser distancia o tiempo): [gist id="1a45efadf25c39ad2b38"] En esta versión del código se puede observar como existen muchas funciones en el $scope que sirven para gestionar el estado de la variable xAxis y para obtener información sobre los gráficos (los valores de las ordenadas y las abcisas y la magnitud seleccionada para el eje x). También teníamos una directiva para los gráficos: [gist id="cdfc8863631144a3cc98"] a la que se le estaba pasando la magnitud seleccionada para el eje x y los datos de los gráficos. A esta versión se llegó después de repetidos cambios del diseño de la página y de varias spikes rápidas. Una vez estuvimos satisfechos con el comportamiento de la UI, decidimos que era hora de estabilizarlo. Para ello creamos una factory de Angular que creaba un objeto JavaScript sencillo que se encargaba de gestionar todo el estado relacionado con los gráficos (la variable xAxis que estaba tanto siendo modificada como leida) y de dar acceso a los datos de los gráficos (que sólo eran leidos): [gist id="5a0d67d473ab757f24ca"] Este widget se creó pasándole los datos registrados (track) al método create. Este método inicializa una serie de valores y devuelve el charts widget que cuenta con acceso privilegiado a varias propiedades y funciones que se han hecho privadas mediante un closure. Básicamente movimos al nuevo widget todas las funciones relacionadas con los gráficos que se encontraban en el controlador haciendo privadas algunas de ellas. Después de haber sido movida al widget, la lógica relacionada con los gráficos se hizo muy fácil de testear usando fake tracks: [gist id="2ac3ac5e1ae177dc7f80"] Después del cambio el controlador se redujo a aproximadamente la mitad: [gist id="641b37c08b0e7c54c0ae"] En este código se puede observar como prácticamente toda la lógica relacionada con la gestión de los gráficos ha desaparacido del controlador. El único vestigio que queda es la creación del charts widget. También pudimos simplificar el código de la directiva sólo con pasarle el nuevo widget (charts): [gist id="faf2736f4dfbc687d04c"] que además absorbió el código que creaba los puntos de los gráficos. Hemos seguido refactorizando de esta manera tanto este como otros controladores de la aplicación, haciendo pequeños refactorings cada vez que detectábamos duplicación o descubríamos nuevos conceptos que podía ayudarnos a agrupar y extraer funcionalidad y estado de ellos. Este es el código actual del controlador que se mostraba en los ejemplos anteriores: [gist id="c90c9a23aea0eac67ad4"] Mantener la complejidad y el tamaño de los controladores de una aplicación Angular bajo control requiere de un esfuerzo constante, que creemos que vale la pena para conseguir un código "habitable" y preparado para adaptarse al cambio (estamos más convencidos aún si cabe después de haber sufrido los efectos negativos de controladores inmensos en aplicaciones Angular legadas). La versión original de este post se encuentra en garajeando.blogspot.com.es.