Buenas Prácticas en Angular

Consejos para no improvisar en Angular

Ignacio Buioli
- 01/11/2018

Como todo en la vida, si hay más de un camino para llegar al mismo puerto es lógico que tengamos preferencia por una cosa u otra. Y en informática siempre habrá un camino más optimo, tanto para el rendimiento como para lo escalable de nuestro proyecto. Angular (A partir de Angular 2) es una plataforma mantenida por desarrolladores que también la utilizan, con lo cual ha ido mejorando con el tiempo. Actualmente, tiene un estado de desarrollo muy sólido, la respuesta a la pregunta ¿conviene aprender Angular? es un "Sí" sin pensarlo. La cuestión acá es ¿cómo implementar Angular de la forma más inteligente posible? ¿Cuales son las mejores prácticas?

Conoce tu Arquitectura

No es posible trabajar con Angular sin tener una idea de la arquitectura de archivos que genera. Especialmente porque necesitamos replicar cierto comportamiento a la hora de crear nuestros componentes, servicios y demás. No da lo mismo dónde y cómo se guarda cada cosa, y va ligado a la utilidad que vayamos a darle.

Situación: Iniciamos un nuevo proyecto de Angular, el código parece prolijo. A los pocos días de desarrollo tenemos mezclados nuestros servicios con componentes que no tienen relación, no sabemos cuál fue el último feature que implementamos y, para colmo, trabajamos en colaboración con otro programador que solucionó un Bug creando un nuevo componente en la carpeta raíz. Este tipo de arquitectura no se sostiene en el tiempo, ni trabajando con GitHub puede mantenerse como sistema de desarrollo. En Angular, lo recomendable, es dividir la arquitectura en tres módulos:


Módulo Core

Aquellos servicios que tendrán una sola instancia de declaración por aplicación (como servicios de autentificación o logeo).


Módulo Recursos Compartidos

Todos aquellos componentes y pipes que no importan ni injectan otros servicios o features en sus constructores. Estos componentes deberían recibir los datos a través de atributos en los templates del componente que los utiliza. El módulo de recursos compartidos no debe tener dependencias del resto de nuestra aplicación. También es el mejor lugar para importar y exportar Angular Material.


Módulos Feature

Lo conveniente es crear un nuevo módulo independiente por cada feature que implementemos en nuestra aplicación. Estos módulos tienen que importar servicios del módulo Core. Si un nuevo feature necesita un servicio de otro feature, lo conveniente es mover dicho servicio al módulo Core.


Compilación con Criterio

No es lo mismo una App de Angular en un teléfono celular, en una web de GitHub o en un servidor de Heroku. Por tal motivo, la compilación no puede valer lo mismo. Desde luego, tampoco consiste en utilizar el siguiente comando de CLI creyendo que todo está solucionado:

ng build

Esta línea hace bastante poco, compila y ya, sin aplicar nada. Sí, es rápido (compila en pocos segundos) y es fácil de recordar, pero el render puede pesar demasiado y la solución es comprender unas simples flags. La verdad es que, antes de ejecutarlo, quedan un par de configuraciones por hacer. En líneas generales, el comando que mejor optimiza una App de Angular (y el recomendado en la mayoría de los casos) es el siguiente:

ng build --prod --aot --builder-optimizer --ouput-hashing=none

Una breve explicación:

--prod: Le pide al compilador utilizar la configuración para el entorno de Producción

--aot: Habilita la compilación Ahed Of Time, necesaria para ciertos motores de render como el futuro Ivy

--builder-optimizer: Espectacular optimizador de los archivos JavaScript, pero puede demorar muchos minutos en compilar un proyecto muy grande

--output-hashing=none: Elimina el número de identificación de los archivos JS y CSS. Suele ser útil en la mayoría de los casos, pero si estamos compilando en un server, y nos encontramos en constante compilado, puede ser recomendable no usarlo o utilizar --output-hashing=all para que el server no utilice los archivos en caché

Como pueden ver, conocer las opciones para compilar en un ecosistema tan inmenso como Angular puede darnos ventajas a la hora de producir nuestra aplicación.

Nombrando tus Funciones, Objetos y Variables. Comentarios legibles.

¿Les pasó clonar un repositorio que necesitaban urgente y, al ver el código, encontrarse con todos los comentarios en Japonés? A mí si. Y en proyectos grandes el Google Translate no ayuda mucho. La realidad es que, se llegó a una convención entre los programadores de que, al menos, sabemos un poco de inglés técnico. Los comentarios en los códigos suelen limitarse a cosas como "no usar esto", "esto es para testear", "esto está bugueado", "esto funciona en linux pero se traba en windows", etc. No son "El Aleph" de Borges, nadie espera (ni quiere) encontrarse con verdadera poesía entre las líneas de un código que necesitamos para implementar la factura electrónica de un cliente. Por ende, nuestros comentarios deben ser breves, claros y, en lo posible, en inglés. El código tiene su estructura en inglés (if, for, else, do, while, let), acompañemos el flujo del idioma al menos para evitarnos quebraderos de cabeza y conseguir un código más escalable en un futuro. Así como no queremos hispanos con las cejas prendidas fuego por comentarios en japonés, tampoco queremos japoneses insultando porque su código está en español. Y sí, es un consejo para programar en general (no solo Angular), pero no está de más repetirlo.

En el caso de cuestiones estrictas de Angular, el mayor problema son los nombres. Angular provee una convención:

En archivos, utilizar el punto seguido del nombre para indicar el tipo de estructura que contiene dicho archivo. Esto se debe a que todas las estructuras de Angular son archivos TypeScript, con la misma extensión. Por ejemplo, si mi componente se llama "Homepage", el archivo que lo contiene debería ser "homepage.component.ts". Y solo debe contener dicha clase (Homepage) por ningún motivo otra (eso de tener dos componentes o un componente y un service en el mismo archivo rara vez tiene sentido). Esto no es algo impuesto (de hecho es posible no seguir estas reglas) pero es la forma más clara de organizar una App, sin contar lo claro que será para un colaborador encontrar las cosas (o para nosotros en un futuro, digamos, 3 meses sin tocar el código de la App). Más información en la guía de estilos de Angular: angular.io/guide/styleguide

Para los nombres de las funciones, variables y objetos pasa lo mismo. Podemos ponerle de nombre "pepito()" a una función que suma dos números, pero quizás sea preferible llamarla "addTwoNums()". Con las variables pasa lo mismo, y se suman muchísimos errores de declaración. Si la variable se va a modificar debe ser declarada con let, sino es conveniente usar const. Esto nos ayudará a debuggear mucho más rápido a la hora de buscar cuando una variable es reasignada.

¿CSS? ¡Mejor SASS! [SCSS, LESS, Stylus]

Como sistema para tener Hojas de Estilo, CSS se ha apoderado del diseño en la Web prácticamente desde que apareció. Y es que, claro, escribir los atributos de diseño en cada una de las tags de HTML era un problema. CSS cambió la forma en la que se diseña en la Web. Sin embargo, estamos a un par de décadas de esa revolución, y las introducciones de cosas como las animation, keyframes, detección de resolución, fuentes de texto, svg, y demás, hacen de un archivo CSS una verdadera experiencia para un desarrollador. Por esta razón nacieron los distintos PreProcesadores de CSS [SCSS, LESS, Stylus] los cuales permiten utilizar variables, bloques condicionales, bloques cíclicos y más extensiones dentro de una hoja de estilo CSS dinámica que luego se procesará en una hoja estática. Angular no está 100% aprovechado sino se hace uso de sus múltiples PreProcesadores (soporta todos los que hemos mencionado hasta ahora sin instalar nada extra). Y con Angular 7 ya no hay excusas, al crear un nuevo proyecto se nos pedirá si queremos trabajar con SASS, SCSS, LESS, Stylus o si queremos seguir en la prehistoria de la Web (CSS, a secas). Nuestro consejo es muy claro: Usen un PreProcesador CSS, su App de Angular, ustedes y quienes toquen su código, se lo van a agradecer.

Implementando Alias para tus Imports

¡Hemos creado nuestros propios servicios y nos sentimos en la novena nube! Pero resulta que hay que importarlos en varios componentes, más algún módulo y otros servicios que dependen de el. Como este servicio será global, decidimos ponerlo en el root de la app. Al importarlo donde lo necesitamos, terminan quedando rutas así:

import { ServiceService } from '../core/service.service';
import { ServiceService } from '../../../core/service.service';
import { ServiceService } from '../../../../../core/service.service';

No solo se ve un poco anti-natural, sino que al modificar la ruta de nuestro service.service.ts, tendremos que ajustarla en todos los archivos donde se use como dependencia. Una tarea de segundos puede convertirse en una odisea de varios minutos (o más) en un proyecto grande. Pero, que tal si hacemos esto:

import { ServiceService } from '@core/service';

¿Se ve mejor no? ¡Podemos hacerlo! Para eso necesitamos modificar un par de líneas del tsconfig.json:

"compilerOptions": {
    "baseUrl": "src",
    "paths": {
      "@core/service": ["app/core/service.service"]
    }
}

Ahora podremos usar un alias que haga referencias a un directorio relativo, que en caso de ser modificada su ruta solo tendremos que actualizarlo una vez en el archivo tsconfig.json para que se aplique en todo el proyecto.

De la vereda de los estilos, cabe la posibilidad de hacerlo con nuestros archivos SCSS, LESS o Stylus. Para esto modificaremos el angular.json (que en versiones anteriores a las 6 estaba oculto como .angular-cli.json):

"stylePreprocessorOptions": {
    "includePaths": ["src", "src/ruta/a/estilos"]
}

Esto lo incluiremos en architect -> build -> options, dentro de nuestro angular.json. Deberemos reemplazar src/ruta/a/estilos por nuestra ruta donde se encuentran nuestros CSS globales (no usar el nombre globals.scss o tendremos un conflicto con dependencias de Angular). Y así ya podremos llamarlo, en cualquier archivo de CSS pre-procesado, de la siguiente forma:

@import 'mi-css-global'

Commits estandarizados + ChangeLog automático = Repositorio saludable

Nos encanta hacer commits, hacer commits es bueno. Podemos ponerle cualquier mensaje a un commit, pero ¿por qué no aprovecharlo y hacer algo superador? Por sí aún es desconocido, existe una convención sobre como hacer commits: www.conventionalcommits.org/en/v1.0.0-beta.2/ la cual es realmente muy útil y aceptada por Angular. Gracias a esta convención, existen herramientas que generan nuestros ChangeLog automáticamente, como por ejemplo esta: github.com/conventional-changelog/standard-version.

Se ajusta muy bien a npm, y es ideal para comunicar a los interesados en nuestra App que nuevas funcionalidades (fix y feat normalmente) incluye la nueva versión. Ahorra tiempo y nos hace nuestro repositorio legible, todo positivo.