Errores en Angular

En las series de artículos que llevamos publicados sobre Angular, tanto en su versión 5 como en la actual versión 6, estamos descubriendo la potencia y prestaciones que este framework nos ofrece. Desarrollar una SPA con Angular es mucho más fácil y rápido que hacerlo «picando código plano», y los resultados son incuestionablemente mejores.

Sin embargo, eso no significa, ni mucho menos, que todo sea un lecho de rosas. En el fascinante mundo del desarrollo web, nada lo es. Hemos escogido una profesión que, para la mayoría de nosotros, es una pasión, el mejor trabajo del mundo. Es tecnología, es arte, es el despliegue de nuestra creatividad y nuestras capacidades llevadas al top. Sin embargo, el desarrollo no está exento de problemas. A menudo se producen errores o problemas que nos ponen piedrecitas en el camino. En ocasiones, esas piedrecitas son verdaderos peñascos. Son los famosos bugs. Cualquiera que se piense que se va a sentar a su teclado, va a estar un par de horas codificando y todo le va a salir perfecto y maravilloso por las buenas, mejor que se dedique a otra cosa.

Ya hemos ido viendo como, en ocasiones, se producen bugs de funcionamiento, porque no hemos tenido en cuenta tal o cual detalle, lo que hace que un enlace funcione sólo a veces, o que una imagen no aparezca donde debe, o cualquier otra incidencia operativa. Ni el mejor desarrollador del mundo podría crear un proyecto sin errores.

En otras ocasiones, los errores se producen porque no conocemos todas las prestaciones o capacidades de la herramienta que estamos usando. Las herramientas actuales (como es el caso de Angular) tienen tantísimas prestaciones, y ofrecen tantísimas novedades, que es imposible dominarlas todas al 100%.

Este artículo está dedicado a recoger los errores o problemas con los que podemos encontrarnos en el día a día con Angular, así como las soluciones adecuadas para cada uno de ellos. Se basa en experiencias propias, y de otros compañeros de profesión. Este va a ser, eso lo tengo muy claro, el artículo que más a menudo actualizaré y, no te quepa duda, el que tú más a menudo consultarás. Espero que te resulte útil.

ERRORES DE COMPILACIÓN

Estos son más habituales de lo que parece. Creas tus módulos, componentes, servicios, enrutadores… toda la estructura de tu aplicación, o bien la descargas desde un repositorio remoto, tipo github o similar. No parece haber errores sintácticos y, se supone, todo debería ir como la seda. Así que tecleas npm start, y el webpack empieza a compilar tu aplicación para mostrarla en el navegador. Sin embargo, las cosas no funcionan, o lo hacen lanzando avisos que no sabes muy bien que consecuencias pueden tener.

DEPENDENCIAS FALTANTES

Uno de los problemas más relevantes, que te impide lanzar tu aplicación, es que te falten dependencias que deberían estar instaladas. Estas pueden ser del propio framework, si obtienes un mensaje similar a este:

You seem to not be depending on "@angular/core" and/or "rxjs". This is an error.

Esto se debe a que, en la aplicación, se está llamando a un elemento de las librerías estándar de Angular que debería estar ahí pero que, por alguna razón, no está. La solución es muy sencilla. Tecleamos en la consola el mandato npm install. Esto hace que se actualicen todas las dependencias, según aparezcan en el fichero package.json. Con esto te queda resuelto el problema.

Una variante de esto es cuando lo que nos falta en el directorio node_modules es un archivo de una librería externa que hemos añadido a nuestra aplicación Angular. La librería está incluida en package.json, los archivos clave de la misma están referenciados en angular.json (o en .angular-cli.json, si estás con versiones anteriores del framwork), pero no se encuentran donde se supone que deberían de estar. El mensaje de error es similar al siguiente:

× 「wdm」: Error: ENOENT: no such file or directory, open 'C:\xampp\htdocs\ang6-base\node_modules\malihu-custom-scrollbar-plugin\jquery.mCustomScrollbar.concat.min.js'

En este último mensaje, el nombre y ruta del fichero que no se encuentra variará dependiendo de qué es lo que nos falte, pero el mensaje es claro.

Una vez más, la solución pasa por ejecutar npm install. Esto descargará de Internet, e instalará en nuestro proyecto, todas las dependencias externas que necesitemos. Por supuesto, deben estar incluidas en package.json. Si, por ejemplo te sale un mensaje como el del ejemplo que te he mostrado, que se refiere a la librería Mailhu Custom Scrollbar (que ya conocemos), pero en tu package.json no aparece la línea

"ngx-malihu-scrollbar": "^1.3.2",

(o similar), deberás teclear, como aprendimos en el artículo correspondiente,

npm install ngx-malihu-scrollbar --save

De todos modos, es muy raro que una librería esté referenciada en angular.json y no en las dependencias de package.json. Eso no suele suceder, a menos que los archivos hayan sido manipulados de forma errónea, pero nunca está de más asegurarte.

LA VERSIÓN DEL CLI

Como ya sabes, cuando vas a empezar a trabajar con Angular, debes instalar en tu ordenador, de modo global, el CLI. Normalmente, a menos que especifiques lo contrario, se instalará la última versión estable disponible. Sin embargo, si tu aplicación te la has descargado de un repositorio remoto, o la has pasado desde otro equipo, es muy posible que la versión local del CLI que se usa en el proyecto sea anterior a la global que tienes en tu ordenador. Al teclear npm start obtendrás un mensaje de aviso similar al siguiente:

Your global Angular CLI version (6.1.4) is greater than your local
version (6.0.8). The local Angular CLI version is used.
To disable this warning use "ng config -g cli.warnings.versionMismatch false".

Esto es sólo un aviso de que la versión del CLI local al proyecto no coincide con la global del equipo. No te va a dar ningún problema (en la mayoría de los casos). Incluso puedes usar la instrucción que te sugiere el mensaje, para que no se te muestre más este aviso, si te resulta molesto. Sin embargo, lo ideal es que ambas versiones coincidan. Para ello, tenemos que actualizar tanto la versión global, como la local.

Para actualizar la versión local, teclearemos las siguientes instrucciones en la consola:

sudo npm uninstall -g angular-cli @angular/cli
npm cache clean --force
sudo npm install -g @angular/cli

Recuerda que la palabra sudo sólo debes usarla si estás empleando Mac o Linux. En Windows no debe teclearse.

A continuación, desde la consola de mandatos, y dentro del directorio raíz de nuestro proyecto, teclearemos las siguientes instrucciones:

rm -rf node_modules
sudo npm uninstall --save-dev angular-cli
sudo npm install --save-dev @angular/cli@latest
sudo npm install

Una vez más, la palabra sudo solo es para usuarios de Mac o Linux.

Con esto, ambas versiones del CLI coincidirán, y no volverás a ver el molesto aviso.

EL COMPILADOR Ahead Of Time

La compilación por defecto en versiones anteriores de Angular era JIT. Para usar la más eficiente compilación AOT debíamos modificar siempre el archivo package.json, sustituyendo la línea

"start": "ng serve -o",

por

"start": "ng serve --aot -o",

Sin embargo, en Angular 6 la compilación es, por defecto, AOT. Esto significa que especificar el modo --aot en package.json es redundante. En la mayoría de los casos, esto no te dará problemas. Sin embargo, con la presencia en nuestro proyecto de ciertas librerías la cosa puede complicarse. Un error que se da con frecuencia es el siguiente. Compilas tu aplicación, todo funciona bien, se abre el navegador, y se carga la aplicación. La pruebas, y todo parece ir bien. Ahora, sin detener el webpack, cambiar cualquier cosa en un fichero (un controlador, una vista, o lo que sea) y grabas. El webpack empieza a compilar de nuevo… y se interrumpe, mostrando un mensaje de error impresionante:

ERROR in ./src/app/app.module.ngfactory.js
Module not found: Error: Can't resolve '@angular/router.ngfactory' in 'C:\xampp\htdocs\ang6-base\src\app'
ERROR in ./src/app/members/members.module.ngfactory.js
Module not found: Error: Can't resolve '@angular/router.ngfactory' in 'C:\xampp\htdocs\ang6-base\src\app\members'

La compilación se detiene, y tu aplicación ya no carga en el navegador. En su lugar, se te muestra el mismo mensaje. Te toca interrumpir el webpack (con CTRL C) y volver a iniciarlo con npm start. Es una molestia, una pérdida de tiempo, y la sensación de que hay «algo» en tu aplicación que no está bien y, lo que es peor, no tienes ni idea de lo que puede ser.

La solución pasa por quitar el flag --aot de la instrucción ng serve en package.json. El problema no volverá a repetirse. Como la compilación es, como te he comentado, por defecto en modo AOT, este flag ya no debe estar ahí.

ERRORES DE LOS ENLACES

De esto hemos hablado múltiples veces en diversos artículos de Angular. Creamos enlaces con la propiedad routerLink (como sabes, el sustittuto de href en proyectos Angular) y no funcionan. Los pulsas y no te llevan a ninguna parte. Voy a insistir en esto aquí, debido a lo habitual que es (no sabéis las consultas que recibo al respecto).

Lo primero de todo, recuerda que, cuando creas una aplicación que va a contar con un un sistema de enrutamiento (es decir, enlaces para navegar por las distintas vistas de la misma), lo que es, en la práctica, el 99,99 % de los casos, al crearla con el comando ng new debes usar el flag --routing true. Además, si vas a crear algún módulo que tenga su propio sistema de enrutamiento, cuando uses el comando ng g m también deberás emplear el mismo flag.

Lo siguiente que tienes que tener en cuenta es que en los archivos de módulo (xxxx.module.ts) de todos los módulos que vayan a contar con enrutamiento deberás incluir el xxxxRoutingModule.  Por ejemplo, en la clave imports de app.module.ts deberá figurar AppRoutingModule. Además, en las importaciones en la parte superior deberás tener la línea siguiente:

import { AppRoutingModule } from './app-routing.module';

En los demás módulos que tengan sistema de enrutamiento las importaciones serán análogas. Por ejemplo, en nuestro CRUD de socios tenemos con enrutamiento el módulo MembersModule. En la clave imports de members.module.ts incluiremos MembersRoutingModule. En las importaciones de la parte superior deberá aparecer la siguiente línea:

import { MembersRoutingModule } from './members-routing.module';

Además, en todos los módulos que impliquen enrutamiento, o que tengan enrutamiento propio, se debe importar el RouterModule de Angular. Detallemos esto usando, como ejemplo, el CRUD de socios en el que estamos trabajando. Lo que aquí comentemos nos servirá para evitar problemas en posteriores proyectos.

En el AppModule tenemos enrutamiento propio (el enrutamiento principal de la aplicación). Está en el fichero app-routing.module.ts. En las líneas de cabecera, donde se hacen las importaciones, encontramos:

import { Routes, RouterModule } from '@angular/router';

Dentro del decorador @NgModule encontramos lo siguiente:

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})

Es decir. El RouterModule es importado, «entra» (clave imports) en el enrutador, y también «sale» (clave exports) para estar disponible para el módulo AppModule.

En MembersModule tenemos una situación similar. Como es un módulo con enrutamiento, contamos con el fichero members-routing.module.ts, que presenta las mismas características, en lo que a RouterModule se refiere.

Un caso diferente es GlobalsModule. Este módulo no tiene enrutador propio. No existe ningún archivo globals-routing.module.ts. Sin embargo, también necesita el RoutersModule. Esto se debe a que uno de los componentes de este módulo es, precisamente, la barra de navegación, con los enlaces correspondientes. Por lo tanto, lo importaremos en globals.module.ts. En las importaciones de la cabecera encontramos:

import { RouterModule } from '@angular/router';

Dentro del decorador @NgModule, en la clave imports, encontramos RouterModule. Como desde aquí ya no debe pasar a ningún sitio, no aparece en la clave exports.

Por último, tenemos el caso de AdminsModule (el que hicimos para que el administrador pueda iniciar o cerrar sesión). Este módulo no tiene enrutamiento propio, y tmapoco tiene los enlaces de la barra de navegación, así que no importa el RouterModule en ninguna parte.

COMO NORMA GENERAL.

  • Los módulos con enrutamiento propio, siempre importarán el RouterModule en el correspondiente archivo de enrutamiento (xxxx-rotuing.module.ts), y lo exportarán desde allí, para que esté accesible para el resto del módulo.
  • Los módulos sin enrutamiento propio, pero en los que alguno de sus componentes cuente con enlaces con la propiedad routerLink importarán el RouterModule en el correspondiente archivo xxxx.module.ts.
  • Los módulos sin enrutamiento propio, en los que ninguno de sus componentes cuente con enlaces dotados de routerLink no necesitarán importar RouterModule aunque, si tienes dudas, puedes importarlo si quieres. No «pide pan».

ERRORES DE LA VERSIÓN DISTRIBUIBLE

Cuando se usa Angular 6 hay algunas prácticas que eran habituales en versiones anteriores, y que ahora tenemos que desterrar. Una de ellas es importar en un módulo otros módulos que se cargan en diferido (carga perezosa). dado que los módulos en diferido no se cargan hasta que no se pulsa un enlace que corresponda a un componente de estos módulos, no deben ser importados por defecto. Te comenté todos los detalles al respecto en un artículo anterior. Si no lo recuerdas, puedes releerlo ahora.

ERRORES EN FORMULARIOS

Hay un detalle cuando montamos componentes con formularios que puede causar que no funcionen correctamente y, si no lo tienes presente, o no tienes costumbre, es bastante difícil de localizar e identificar. Si tu formulario emplea doble binding (el conocido como «banana in a box») u otras características específicas de los formularios en Angular, es importante que, en el controlador del módulo donde se aloja el componente (xxxx-module.ts) tengas importada el módulo FormsModule, de @angular/forms, así:

Ante la duda y, como no pide pan, si vas a usar algún formulario en el módulo y no sabes si vas a necesitar o no el FormsModule, impórtalo, y te quitas de problemas.

   

Deja un comentario