Introducción al sistema de Routing en Angular

  • Por
Qué es el sistema de routing, qué elementos contiene y cómo configurar las primeras rutas en una aplicación Angular.

En este artículo vamos a comenzar con una serie de entregas del Manual de Angular en las que vamos a abordar el sistema de routing, uno de los actores principales de las aplicaciones Angular.

El sistema de routing no es un asunto trivial, ya que envuelve a muchas clases, objetos, componentes y configuraciones. De hecho es algo bastante sofisticado, ya que tiene decenas de configuraciones para realizar rutas de todo tipo. No obstante, comenzar a trabajar con él en un primer ejemplo no es tan complicado, como podrás ver a continuación.

Qué es un sistema de routing

Pensando en aquellas personas que comienzan con Angular su incursión en las "SPA" (Single Page Application), comenzaremos aclarando qué es un sistema de routing y por qué lo necesitamos.

En cualquier sitio web generalmente tienes varias direcciones que son entregadas por un servidor, para mostrar diferentes contenidos del sitio. Podemos tener una portada, una página de productos, una de contacto, etc. Cada una de esas páginas se presenta en una ruta diferente del sitio web, que podrían ser como example.com, example.com/productos/index.html, example.com/contacto.html, etc. Cada una de esas rutas podría tener un archivo HTML, que se sirve con el contenido de esa sección. Hasta aquí estamos seguros que los lectores no tendrán ningún problema, pues es así como funcionan, en líneas generales, prácticamente todos los sitios web.

Sin embargo, en las aplicaciones Angular sólo tenemos una página, el index.html y toda la acción se desarrolla dentro de esa página. En Angular lo común es que el index sólo tenga un componente en su BODY y realmente toda la acción se desarrollará en ese componente. Todas las "páginas" (pantallas o vistas) del sitio web se mostrarán sobre ese índex, intercambiando el componente que se esté visualizando en cada momento.

Nota: Ese es básicamente el concepto de Single Page Application, del que puedes obtener más información en un artículo genérico sobre SPA, que explica con mayor detalle este modo común de desarrollo de aplicaciones con Javascript.

Para facilitar la navegación por un sitio donde realmente sólo hay un index, existe lo que llamamos el sistema de routing, que tiene el objetivo de permitir que en el sitio web haya rutas internas, respondiendo a rutas "virtuales" como las que existen en los sitios tradicionales.

Llamamos "virtuales" a esas rutas, porque realmente sólo existe un "index.html", no habrá un archivo "contacto.html" o "productos.html" para cada ruta, sino que será realmente siempre el "index.html" el que se entregue al navegador.

El sistema de routing es el encargado de reconocer cuál es la ruta que el usuario quiere mostrar, presentando la pantalla correcta en cada momento. Esto es útil por varios motivos, entre ellos:

  1. Permite que la aplicación responda a rutas internas. Es decir, no hace falta entrar siempre en la pantalla principal de la aplicación y navegar hasta la pantalla que queremos ver realmente.
  2. Permite que el usuario pueda usar el historial de navegación, yendo hacia atrás y hacia adelante con el navegador para volver a una de las pantallas de aplicación que estaba viendo antes.

El sistema de routing de Angular

Angular, como un buen framework, dispone de un potente sistema de routing para facilitar toda la operativa de las single page applications. Está compuesto por varios actores que tienen que trabajar juntos para conseguir los objetivos planteados.

Comenzaremos explicando el sistema de routing resumiendo los elementos básicos que forman parte de él y que son necesarios para comenzar a trabajar.

  • El módulo del sistema de rutas: llamado RouterModule.
  • Rutas de la aplicación: es un array con un listado de rutas que nuestra aplicación soportará.
  • Enlaces de navegación: son enlaces HTML en los que incluiremos una directiva para indicar que deben funcionar usando el sistema de routing.
  • Contenedor: donde colocar las pantallas de cada ruta. Cada pantalla será representada por un componente.

Crear nuestra primera aplicación con rutas

Ahora que tenemos claro qué es el sistema de routing y sabemos qué elementos principales vamos a necesitar para ponerlo en marcha, vamos a crear una especie de receta para poder crear una aplicación, desde cero, en la que podamos incorporar rutas sencillas.

Nota: para comenzar esta práctica damos por hecho que tienes una aplicación vacía, en la que solo tenemos el módulo principal (app.module.ts) y el componente principal "app.component.js".

1.- Importar el código del sistema de routing

En nuestro módulo principal tenemos que hacer los correspondientes imports.

Deberías incluir a RouterModule y Routes, que obtenemos de la librería "@angular/router". El código será como este:

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

RouterModule es el módulo donde está el sistema de rutas, por tanto contiene el código del propio sistema de rutas.

Routes es una declaración de un de tipo, que corresponde con un array de objetos Route. Los objetos Route están declarados por medio de una interfaz en el sistema de routing. Esta interfaz sirve para que en la declaración de tus rutas coloques solamente aquellos valores que realmente son posibles de colocar. Si no lo haces así, el compilador de TypeScript te ayudará mostrando los correspondientes errores en tiempo de desarrollo.

Nota: Deberías estar esperando que metamos el RouterModule en el imports del decorador del módulo principal, pero para hacer esto aún tenemos pendiente un paso intermedio.

2.- Crear la lista de las rutas disponibles en la aplicación

En este paso tenemos que crear un array de las rutas que queremos generar en nuestra aplicación. Utilizaremos la declaración del tipo Routes, que hemos dicho es un array de objetos Route.

Cada objeto Route del array tiene un conjunto de campos para definir la ruta, definido por la interfaz Route, siendo que lo general es que tenga al menos un camino "path" y un componente para representar en esa ruta.

El código que producirás será más o menos así.

const rutas: Routes = [
  { path: '', component: HomeComponent },
  { path: 'contacto', component: ContactoComponent }
];

Ese código también lo puedes colocar en el módulo principal, aunque nada te impide organizarlo mejor y colocarlo en un archivo aparte, exportando la constante "rutas" (el array) y luego importando desde el módulo principal.

Como puedes ver, los caminos (propiedad path) se definen con cadenas. La cadena vacía "" es el home de nuestra aplicación (URL como example.com). La cadena "contacto" corresponde a la ruta "/contacto" (URL como example.com/contacto).

Luego se indica el componente que debe mostrarse para cada ruta (propiedad component). Esos componentes podemos considerarlos como las pantallas de la aplicación. Tendremos uno por ruta, de modo que, cuando se acceda a tal ruta, Angular mostrará tal componente, y quitará el componente anterior que se estuviera mostrando.

Nota: obviamente ese componente debe formar parte del módulo o de otro módulo importado, es decir, el componente que representa la ruta debe ser conocido para poder usarse, como cualquier otro componente en general. Como ya sabes crear componentes creemos que no necesitarás muchas explicaciones. Pero generar esos componentes es tan sencillo como lanzar los comandos del CLI: "ng g c home" y "ng g c contacto". Si todo ha ido bien, esos componentes se habrán creado en el módulo principal y se habrán agregado en los imports del módulo y en el decorador, en el array "declarations". Por supuesto, puedes crear esos componentes en el módulo que mejor te venga.

3.- Declarar el sistema de routing en el "imports" del decorador @NgModule

Ahora tienes que editar el decorador del módulo principal @NgModule, donde tendrás que colocar en el array "imports" la declaración de que vas a usar el sistema de routing de Angular.

Tenemos que indicar a RouterModule, pero en ese imports necesita la configuración de rutas, creada en el array Routes[] del paso anterior. De momento, esa configuración la cargamos mediante un método forRoot() en el que pasamos por parámetro el array de rutas.

Se ve mejor con el código. Este es el decorador completo, pero te tienes que fijar especialmente en el array "imports".

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    ContactoComponent
  ],
  imports: [
    BrowserModule,
    RouterModule.forRoot(rutas)
  ],
  providers: [],
  bootstrap: [AppComponent]
})

Con esto hemos terminado el trabajo en el módulo principal. Pero aun nos queda implementar la vista en el componente principal para generar los enlaces a las rutas y el contenedor donde se tienen que mostrar los componentes.

4.- Definir el template del componente principal

Comenzamos el trabajo en el componente principal. En este componente tenemos que meter un poco de código en la vista, archivo "app.component.html".

En el template se crea el navegador de las rutas, indicando en los enlaces la directiva "routerLink", con el valor de cada ruta.

<nav>
  <a routerLink="/">Home</a> | 
  <a routerLink="/contacto">Contacto</a> | 
</nav>

No puede haber ningún enlace generado, que no tenga su correspondencia en el sistema de rutas. Fíjate que los enlaces comienzan por una "/" para hacerlos absolutos a la raíz del dominio.

Por último debes colocar un contenedor llamado "router-outlet" para definir el espacio donde se van a inyectar los componentes. Es tan sencillo como colocar esta etiqueta definida por el módulo del sistema de routing.

<router-outlet></router-outlet>
Nota: Aunque parezca un componente, "router-outlet" es una directiva, ya que su función es manipular el DOM para inyectar un componente u otro frente al cambio de una ruta.

Con eso es todo. Ahora si abres tu aplicación deberías ver las rutas funcionando.

Problemas comunes con el sistema del routing

Lo que hemos hecho hasta ahora es poco, por lo que no debería haber muchos problemas con nuestro ejemplo, pero vamos a ver un par de posibles errores que nos podemos encontrar.

Colocar un enlace a una ruta inexistente

Si nos equivocamos al escribir en un enlace la ruta, obtendremos un mensaje de error al hacer clic sobre ese enlace.

Por ejemplo escribimos "contato" en vez de "contacto" en la ruta.

<a routerLink="/contato">Contacto</a> |

Este es el error que vamos a encontrar: Error: Cannot match any routes. URL Segment: 'contato'

Colocar un componente no existente o no accesible en el módulo

Hasta ahora las rutas estaban asociadas a un componente. Puede ocurrir que en la declaración del array de Routes indiques un componente que no existe, ya sea porque has escrito mal el nombre de la clase del componente, o porque estás intentando usar un componente que no está en este módulo o que no está importado por este módulo.

Por ejemplo, podemos colocar el nombre de un componente que no existe como ruta:

{ path: '', component: HComponent },

En este caso el editor probablemente te alertará del error, señalando con una línea en rojo, pero si no lo ves, el compilador de TypeScript será el que te avise. El mensaje de error que recibiremos será algo como: Cannot find name 'HComponent'.

Eso era porque HComponent no existía, pero podría ocurrir que el componente sí que exista, lo hayas importado, pero no esté disponibe en el módulo principal. Entonces el error sería algo como "CompNoImportadotComponent is not part of any NgModule or the module has not been imported into your module".

Equivocarse en la ruta en la declaración de rutas

Otra cosa que te puede ocurrir es que coloques una "/" al pricipio de una ruta. Fíjate que en la configuración de las rutas la ruta raíz es "" (cadena vacía) y no "/".

Esto sería incorrecto:

const rutas: Routes = [
  { path: '/', component: HomeComponent },
  { path: '/contacto', component: ContactoComponent }
];

Para advertirte que las rutas no comienzan por "/", Angular te mostrará el siguiente error en la consola del navegador: "Invalid configuration of route '/': path cannot start with a slash".

La verdad es que el editor te ayuda mucho para no cometer errores, gracias al soporte de TypeScript, además que los errores de Angular son bastante claros. Si no encuentras aquí tu problema y no eres capaz de entenderlo, siempre puedes googlear ;).

Conclusión

Esta introducción al sistema de routing de Angular ha abordado tanto la teoría relacionada con la necesidad de uso de un sistema de routing, como la práctica en su implementación en Angular.

No obstante, como ya hemos comentado, el sistema de routing es muy complejo y tiene muchas otras cosas que debemos aprender para sacarle todo el partido y cubrir necesidades comunes de las aplicaciones. Seguiremos abordando las rutas y su configuración en los próximos artículos del manual.