Iniciación al enrutamiento

En el artículo anterior hemos trabajado mucho, y aún no tenemos nuestra aplicación operativa. Ni siquiera podemos ver las vistas de los componentes que hemos creado, y los enlaces de la barra de navegación aún no funcionan (y ni siquiera los tenemos todos).

En este artículo vamos a abordar el enrutamiento (al menos, lo básico, ya que el enrutamiento en Angular da para más), y lograremos que nuestra aplicación funcione, al menos parcialmente (es la idea, porque hay aspectos que aún no veremos en este artículo; no te preocupes).

El enrutamiento en Angular es algo complejo al principio. Sin embargo, una vez que lo dominas, verás que es muy potente y da mucho juego. A lo largo de los artículos de esta sección aprenderemos algunas cosas que nos dejarán claro por qué se emplea Angular para crear SPA’s, a pesar de toda esta aparente complejidad. Ánimo, que vamos a ello.

CREANDO EL ENRUTAMIENTO RAÍZ

Vamos a empezar creando el enrutamiento. El sistema de enrutamiento de Angular se basa en crear una relación de lo que se puede poner en la barra de direcciones, y a que componente llegaremos según lo que se ponga. Se pueden crear enrutamientos globales a la aplicación, que nos permitirán acceder a todas o la mayoría de las secciones, y enrutamientos de módulo, que se crean en un módulo específico para acceder a las vistas de distintos componentes de ese módulo. En este artículo vamos a empezar trabajando con el enrutamiento raíz.

Como es lógico, todo lo que tenga que ver con la raíz del sitio estará en AppComponent, o muy íntimamente relacionado con dicho componente. En concreto, para crear el enrutamiento raíz iremos al archivo app-routing.module.ts. Ya el nombre nos dice que tiene que ver con el enrutamiento. Su aspecto inicial es el siguiente:

Como ves, es algo muy similar a la estructura de los TypeScripts que ya conocemos, con algunas pequeñas diferencias (vale, pequeñas, pero muy significativas).

Empieza por observar las importaciones que hace en las líneas 1 y 2. Se importa, del núcleo de Angular, el NgModule, que nos permite luego crear el decorador. Luego se importan Routes y RouterModule. RouterModule es un módulo como los que creamos nosotros, pero que ha sido creado por el propio Angular, para hacer funcionar el sistema de enrutamiento. Routes es una matriz de un tipo personalizado de variable que se define en el propio Angular y que se llama Route. Dónde se define y cómo no lo vamos a ver ahora. Simplemente, asumimos que es eso. Un tipo personalizado de datos de TypeScript. Si quieres saber más sobre como crear tiposs personalizados de datos en TypeScript, puedes ver este artículo.

Observa la línea resaltada (la 4). Define una matriz donde crearemos las rutas a las distintas vstas de nuestra aplicación. En seguida nos meteremos con ese punto.

En el decorador vemos que este archivo no tiene propiedad declarations, y no la necesita. Solo importa el módulo RouterModule y lo exporta. Observa que en la importación le añade el método forRoot(routes). El argumento routes es la matriz que se define más arriba, en la línea 4. El método forRoot() indica que este enrutamiento es para la raíz del sitio. Los enrutamientos de otros módulos (ya lo veremos) emplean un método parecido que se llama forChild(). De momento, no pienses mucho en eso.

Vemos que se finaliza el enrutamiento exportando toda la clase, como ves en la última línea.

Por lo tanto, se importa el módulo de enrutamiento, se definen las rutas en la matriz routes y se exporta el enrutamiento con esas rutas definidas. Y ¿a dónde se exporta? ¿Recuerdas la vista de AppComponent? La última línea es <router-outlet></router.outlet>. Esto, que parece una etiqueta HTML personalizada de Angular es, en realidad, lo que se conoce como una directiva. Las directivas son instrucciones especiales de Angular que se integran en los componentes para ampliar las funcionalidades del HTML. En concreto, esta directiva «recibe» el enrutamiento, y permite que funcione.

CREANDO LA MATRIZ DE LAS RUTAS

Vamos a modificar la matriz routes para que podamos acceder, de momento, a la pagina principal del sitio, y a la vista del listado de clientes y de proveedores (luego ya añadiremos accesos para más módulos y componentes). El app-routing.module.ts nos queda así:

En las líneas resaltadas ya vemos cambios importantes. Empecemos por mirar el primer bloque (líneas 4, 5 y 6). Como vamos a enrutar tres módulos (HomeComponent, que será nuestra vista principal, CustomersListComponent, que será la lista de clientes y SuppliersListComponent, que será la lista de proveedores) lo primero que tenemos que hacer es importar esos componentes. Para que esto funcione correctamente, dichos componentes deberán ser exportables y los módulos que los contienen deberán estar importados en app.module.ts (esto último ya lo hemos comentado en el artículo anterior).

ATENCIÓN. Si estás usando el VSC con las extensiones adecuadas, cuando crees un objeto route y escribas el nombre del componente te hará la importación automáticamente, por lo que te evitas escribir la instrucción import correspondiente.

A continuación, dentro de la matriz routes incluimos tres objetos en notación JSON, que se refieren a las rutas a través de las cuales se alcanzarán los tres componentes. Observa que cada objeto tiene dos propiedades: path, que es donde se define la ruta que habrá que teclear en el navegador para acceder a un componente, y component, que será el componente al que se accederá a través de dicha ruta. Como nuestra aplicación arranca en localhost:4200, la ruta que especifiquemos en path será a partir de ahí.

Por ejemplo, para entendernos, fíjate en la primera ruta. El valor de path es una cadena vacía. Eso quiere decir que cuando en la barra de direcciones aparezca localhost:4200, sin nada más, se cargará HomeComponent. Es decir, nuestra vista de inicio ya no será AppComponent, sino HomeComponent.

Si tecleamos en la barra de direcciones localhost:4200/clientes se cargará en el viewport la vista de CustomersListComponent, porque ese es el path y component definido en el segundo objeto de routes. Por lo mismo, si tecleamos localhost:4200/proveedores se cargará SuppliersListComponent.

Observa un ejemplo en la imagen a continuación:

Fíjate en la ruta que hay en la barra de navegación, y en que la vista que se carga ya no es la de AppComponent, sino la de HomeComponent. ¿Correcto? Pues no del todo. Es cierto que se carga la vista de HomeComponent (lo vemos por el texto Página principal de la aplicación, que se define en home.component.html). Sin embargo, también se carga la cabecera y la barra de navegación, que están insertadas en AppComponent, y no en HomeComponent. Es decir, AppComponent se cargará siempre, sí o sí, con independencia de que, además, se cargue otro componente. Y esa es la razón por la que en app.component.html no metemos código HTML, sino sólo aquello que queramos incluir en todas las vistas de la aplicación (como la barra de navegación y la cabecera de página). Si estos elementos los quisiéramos sólo en algunas vistas, pero no en otras, tampoco los incluiríamos en app.component.html, sino en cada una de las vistas donde los necesitemos.

ATENCIÓN. Esta es una cuestión de convencionalismo. El AppComponent se carga siempre porque, en app.module.ts (que es el módulo principal de la aplicación), tenemos una línea como la siguiente:

bootstrap: [AppComponent]

Por supuesto, esto podríamos cambiarla, haciendo que el componente que se cargue siempre indefectiblemente sea otro. Sin embargo, no es una buena práctica cambiar el componente de arranque de la aplicación porque, como digo, es un convencionalismo aceptado de forma casi universal.

Si ahora, en la barra de navegación, tecleamos localhost:4200/clientes, veremos la vista del componente CustomersListComponent. Pruébalo. Y a continuación, vamos a seguir haciendo las cosas que nos faltan (por ejemplo, la página de error 404 y los enlaces de la barra de navegación, que aún no nos funcionan).

LA PÁGINA 404

Tenemos definida una vista específica para los errores 404 (página no encontrada), en NotFoundComponent, pero aún no tenemos ninguna forma de que esta vista se cargue cuando el usuario teclea, en la barra de direcciones, una página que no existe en nuestro sitio. Vamos a ampliar un poco más el listado de rutas en app-routing.module.ts:

Vamos a fijarnos en la línea 7, donde importamos el NotFoundComponent, para que esté disponible para el enrutador. Ahora mira las líneas de la 22 a la 25. Esto hace que si el usuario teclea localhost:4200/404 se cargue la vista de NotFoundComponent. Sin embargo, no es normal que un usuario teclee eso. Lo normal es que un error de este tipo venga definido por algo como localhost:4200/cualquiercosa. En ese caso entra en juego el último objeto de ruta definido, en las líneas de la 26 a la 29. Cuando ponemos, en la propiedad path, el valor '**' significa que cualquier dirección que se teclee y no coincida con los path definidos previamente, activará este componente. Sin embargo, en este objeto no tenemos la propiedad component, sino redirectTo. Eso hace que si lo tecleado en la barra de direcciones no coincide con ningún path anterior (lo que quiere decir que coincide con '**') se redirija al path definido como '404'.

Y claro. seguramente estarás pesando que estos dos últimos objetos de ruta se podrían haber «fundido» en uno solo, que fuera así:

Y tienes toda la razón. Lo único que quería era enseñarte que en un objeto de ruta no tiene por qué existir siempre la misma estructura de propiedades. Existen otras propiedades, que iremos viendo a lo largo de este curso. Desde luego, en este caso concreto, es mucho más elegante y óptima la segunda versión.

Sólo recuerda que path: '**' es un comodín, por lo que debe definirse al final de todos los objetos de ruta, de modo que sólo «entre» aquí si no ha habido coincidencias anteriortes.

LOS ENLACES DE LA BARRA DE NAVEGACIÓN

Aún tenemos nuestra barra de navegación con sólo tres enlaces, aunque tenemos más componentes. Eso ahora no importa. Ya añadiremos los que nos faltan. Lo que sí que importa es que los enlaces que tenemos aún no funcionan, y vamos a hacerlos funcionar ahora.

Lo primero que tenemos que hacer es ir a donde tenemos definido el HTML de la barra de navegación, es decir, en navbar.component.html. En este componente sustituiremos, en todos los enlaces, la propiedad href por routerLink. Esta no es una propiedad normalizada de HTML, y esto no nos funcionaría en una barra de navegación que fuera parte de un proyecto no Angular. La propiedad routerLink es propia de Angular, y se ha diseñado para que los enlaces «casen» con los objetos route que hemos definido en el enrutador.

A continuación, a la propiedad routerLink le asignaremos, en cada enlace, el valor que coincida con la propiedad path de la ruta correspondiente. El navbar.component.html nos quedará, por lo tanto, así:

Observa, en las líneas resaltadas, como quedan los enlaces. Luego mejoraremos algo su comportamiento pero, por ahora, si los pruebas, verás que funcionan perfectamente.

ATENCIÓN. Es posible que encuentres aplicaciones Angular donde se emplee la propiedad estándar href, en lugar de routerLink. En muchos casos, funcionará, aparentemente, igual, pero sólo aparentemente. Internamente, Angular espera routerLink, no href y, en algunos casos, puede darte errores. Por lo tanto, mejor no hagas experimentos con gaseosa y emplea la propiedad que Angular pone a tu disposición para este fin.

 

MUCHA ATENCIÓN A ESTO. Cuando creamos una barra de navegación, esta es un componente que, como norma general, formará parte de un módulo. En este ejemplo le hemos llamado CommonsModule, pero puede tener otro nombre. Al ser un componente que va a ser común a toda la aplicación (o a gran parte de ella), estará en un módulo llamado CommonsModule, o GlobalsModule, o algún otro nombre similar que haga referencia a componentes globales. La cuestión es la siguiente: en la lógica del módulo (en nuestro caso, commons.module.ts) debemos importar el modulo RouterModule de Angular. En las líneas de importación que se colocan al principio del archivo deberemos tener una línea como la siguiente:

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

Además, en el decorador @NgModule, en la propiedad imports deberemos tener, entre otras, la importación de RouterModule.

Si no lo haces así, tus enlaces no funcionarán (y, creéme, se vuelve uno loco intentando averiguar por qué).

CONCLUYENDO

En este artículo hemos visto lo básico sobre enrutamientos para nuestra aplicación SPA. Aún no es gran cosa, pero ya vamos estando familiarizados con la estructura que tienen las aplicaciones creadas con Angular 5. Sin embargo, aún debemos seguir aprendiendo sobre este tema, y lo haremos a partir del siguiente artículo. La aplicación, en su estado actual, la tienes en este enlace.

   

Deja un comentario