Después de una introducción al Routing en la parte #1 de esta serie, es el momento de dar estilo a nuestros links y presentar la navegación programática. ¡Vamos allá!
Uso del routerLinkActive para dar estilo a un link activo
Como habrás notado, aunque al hacer clic en una pestaña nos lleve a la página correcta, el comportamiento de la pestaña no es el adecuado, ya que, para empezar, la opción de Home siempre se queda marcada. Esto ocurre porque la clase active está siempre presente en el primer <a> del app.component.html. La clase active no es más que una clase dada por Bootstrap que hace que una pestaña se muestre como "seleccionada".
Pero ahora la clase active está incluida de forma estática en ese <a>, y lo que necesitamos es que se añada dinámicamente. Para eso, Angular nos ofrece una directiva llamada routerLinkActive, que puede ser añadida tanto en el elemento HTML que engloba a un link como al link en sí mismo. Veamos cómo funciona.
1. Eliminamos la clase active y añadimos el routerLinkActive al <a>. La directiva espera una clase de CSS entre las dobles comillas, así que le pasamos la clase active.
2. Añadimos la clase con el routerLinkActive a todos los <a>.
En teoría, los links ya deberían mostrar el estilo correcto, pero si guardas y vas a tu navegador, verás que no es así, ya que la pestaña Home está siempre seleccionada.
El problema es que el routerLinkActive analiza el path en el que te encuentres actualmente y comprueba qué links tienen rutas asociadas a ese path. Por defecto, la directiva añade la clase de CSS si el link contiene cualquier elemento del path en el que te encuentras. O, dicho de otra manera, si ese link es parte del path en el que te encuentras.
Entonces, lo que ocurre con el link de la Home:
es que ese valor (una ruta vacía) siempre es parte del path del resto de pestañas:
http://localhost:4200/
http://localhost:4200/servers
http://localhost:4200/servers
Ahí está la barra, siempre presente. Es como si la Home estuviese representada en todos los paths. Pero esto tiene arreglo, ya que podemos añadir una configuración especial .
3. En el mismo elemento HTML donde hemos añadido el routerLinkActive, añadimos otra directiva llamada routerLinkActiveOptions, que debemos vincularla con property binding, porque espera un objeto de JavaScript (en adelante, JS) y no un simple string.
4. Le pasamos la propiedad reservada exact y le damos el valor de true. Con esto le estamos diciendo a Angular que sólo añada la clase active si el path coincide exactamente con el link al que está vinculado. Recuerda:
- link: routerLink="/"
- path: http://localhost:4200/
- link: routerLink="/"
- path: http://localhost:4200/servers
Esto no sería un 100% match
- link: routerLink="/servers"
- path: http://localhost:4200/servers
100% match
- link: routerLink="['/users']"
- path: http://localhost:4200/users
100% match
¡Problema solucionado!
Navegación programática
Después de aprender cómo crear y cargar rutas, vamos a aprender a programar rutas de manera que se activen cuando ocurra algo en el código. Por ejemplo, cuando un usuario hace clic en un botón, cierto código se ejecuta y a continuación, el usuario es re-dirigido a otra página. Esto es la navegación programática. En este curso es donde aprendí todo sobre ella.
Supongamos que en el home.component.html necesitamos un botón que haga algo cuando el usuario haga clic y después, se le re-dirija a la página de Servers.
1. Creamos el botón al final del archivo HTML. Podríamos usar routerLink, pero no vamos a hacerlo, porque vamos a simular que necesitamos hacer algo más que simplemente navegar hasta la página Servers. Así que añadimos un click event y lo vinculamos a un método llamado, por ejemplo, onLoadServers.
2. Implementamos el método en el home.component.ts. Aquí es donde resolveríamos algún código complejo, por ejemplo, podríamos llamar a un backend, almacenar algo en un servidor y luego redirigir al usuario a la página de Servers. Supongamos que esto ya ha pasado y que ahora necesitamos configurar el paso de navegar hasta la página de Servers.
ngOnInit() { } onLoadServers() { // complex code that connects to a backend // navigation to Servers page }
Para eso,
debemos tener acceso al Router de Angular, para así indicarle nuestra intención de navegar a alguna ruta.
Podemos hacerlo simplemente inyectando el Router en el constructor. Le damos el nombre de router, por ejemplo, y lo transformamos en una propiedad. No olvides importar el Router desde angular/router.
Hecho esto, ya podemos usar la propiedad router, que nos ofrece varios métodos interesantes. El que nosotros necesitamos es navigate, que acepta un argumento para permitirnos navegar hasta una ruta nueva. En este caso, una "ruta" se define como un array que contenga los elementos que componen el nuevo path.
constructor(private router: Router) { } ngOnInit() { } onLoadServers() { // complex code that connects to a backend // navigation to Servers page this.router.navigate([]) }
Es un concepto muy similar a lo que explicábamos en la parte #1 sobre el Router Link:
El primer elemento del array será el primer segmento del path. Por tanto, si queremos navegar hasta Servers, le pasamos la ruta /servers (path absoluto).
this.router.navigate(['/servers']);
¡Y listo! Si ahora guardas y haces clic en el botón Load servers, verás que te lleva a la página Servers, sin recargar la página (igual que al usar el Router Link), dando así una experiencia de navegación óptima.
Uso de los relative paths en la navegación programática
Vamos a ver cómo usar los relative paths en la navegación programática. Para eso, añadimos un botón de ejemplo en el servers.component.html, antes del custom element <app-edit-server>. Al igual que en el ejemplo de antes, supongamos que por algún motivo necesitamos un botón ahí para que recargue la página Servers.
1. Creamos el botón y le añadimos un click event vinculado a un método llamado, por ejemplo, onReload.
2. Vamos al servers.component.ts e inyectamos el Router en el constructor, creando una propiedad (router) para poder usarlo.
3. Creamos el método onReload, usamos ahí el método navigate del router y le pasamos la ruta servers como path absoluto.
constructor(private serversService: ServersService, private router: Router) { } ngOnInit() { this.servers = this.serversService.getServers(); } onReload() { this.router.navigate(['/servers']); }
Si guardas y pulsas el botón Reload Servers, verás que no pasa nada, ni la consola nos devuelve ningún error.
Además, como ya vimos, Angular no hace ninguna petición al servidor, así que el icono de recargar la página nunca se pone a dar vueltecitas.
¿Pero qué pasa si convertimos el path absoluto en uno relativo?
this.router.navigate(['servers']);
Verás que el funcionamiento es el mismo
La razón por la que esta técnica funciona con el método "navigate" y no con el "routerLink", es porque, al contrario que el "routerLink", el método "navigate" no sabe en qué ruta actual nos encontramos.
Entre otros motivos, porque el routerLink se usa directamente en la template de un componente, por tanto, siempre sabrá cuál es la ruta actual.
Es cierto que el método navigate también lo estamos usando dentro de un componente, pero no en su template, sino en su código TypeScript (en adelante, TS). Con esto, Angular no tiene suficiente información para determinar la ruta actual. Para averiguarla, el método navigate necesita un segundo argumento: un objeto de JS donde podremos configurar el comportamiento de la navegación.
Volveremos a esto más adelante, pero de momento, basta con saber que podemos añadir una propiedad llamada relativeTo, que espera que le informemos sobre a qué ruta debe ser relativa el link que le pasamos en el primer parámetro. Si no le indicamos nada, el comportamiento por defecto es usar la ruta raíz, es decir, http://localhost:4200. Por eso hasta ahora no nos da ningún error, porque la ruta raíz + el path "servers" es convertido por Angular en http://localhost:4200/servers, ruta que, como sabemos, sí existe
A la propiedad relativeTo debemos pasarle la ruta actual en la que nos encontremos, que previamente debemos inyectar en el constructor. Para obtener esa ruta, Angular nos da una herramienta muy conveniente, el ActivatedRoute, que debe ser importado desde el angular/router. Para usarla, le damos un nombre, por ejemplo route, y la convertimos en una propiedad de TS.
constructor(private serversService: ServersService, private router: Router, private route: ActivatedRoute) { } ngOnInit() { this.servers = this.serversService.getServers(); } onReload() { this.router.navigate(['servers'], { relativeTo: this.route }); }
Si ahora guardas y vas a tu navegador, comprobarás que hemos roto la app, ya que Angular está intentando encontrar la ruta http://localhost:4200/servers/servers, la cual no existe
// this.router.navigate(['servers'], { relativeTo: this.route });
Trabajar con paths relativos y absolutos es una de las múltiples cosas que aprendí haciendo este completo curso de Angular.
Cómo pasar parámetros a una ruta
Vamos a mejorar un pelín nuestra app incluyendo más rutas en el app.module.ts. Supongamos que, a parte de los users, también queremos cargar el componente User, que haría referencia a un usuario individual.
{ path: 'users', component: UsersComponent }, { path: 'users', component: UserComponent },
Para eso, lo ideal sería cargar un usuario de manera dinámica, por ejemplo, pasándole el id de un usuario. Recordemos que hemos asignado un id a cada usuario en el users.component.ts.
Una alternativa sería pasarle el id directamente al path, pero esto no sería muy dinámico.
{ path: 'users/1', component: UserComponent }, { path: 'users/2', component: UserComponent }, { path: 'users/3', component: UserComponent },
En lugar de eso,
podemos añadir parámetros a nuestras rutas, que pasan a ser segmentos dinámicos de un path.
Sintaxis
Los dos puntos ( : ) son lo que diferencia un segmento dinámico de uno estático, nos indican que estamos ante un segmento dinámico del path. Esto nos permitirá rescatar el parámetro contenido en el componente cargado usando el nombre que le espefiquemos en el parámetro (en el ejemplo, nombreDelSegmento). Para el caso que nos ocupa, podemos llamar al parámetro "id".
{ path: 'users/:id', component: UserComponent },
Al añadir los dos puntos, Angular cargará el UserComponent cuando escribamos cualquier cosa después de http://localhost:4200/users/. Por ejemplo, si escribimos literalmente http://localhost:4200/users/contenidoDinámico, nos llevará al componente User. Cualquer cosa después de la última / constituye ahora el parámetro id. Por tanto, con este cambio hemos conseguido cargar el componente User con una ruta dinámica, aunque aún tengamos mucho que pulir
THE END!
¡Y hasta aquí la parte #2 de esta guía completa sobre Routing! Espero que hayas aprendido algo nuevo
Sobre la autora de este post
Soy Rocío, una abogada reconvertida en programadora. Soy una apasionada de aprender cosas nuevas y ferviente defensora de que la única manera de ser feliz es alcanzando un equilibrio entre lo que te encanta hacer y lo que te saque de pobre. Mi historia completa, aquí.
Más recursos de aprendizaje
En mi experiencia, la manera más eficaz para aprender Angular es combinando varias vías de aprendizaje. Uno de mis métodos favoritos son los vídeo-cursos y mi plataforma predilecta para eso es Udemy. He hecho varios cursos pero sólo recomiendo aquellos que verdaderamente me han sido útiles. Aquí van:
Si necesitas apoyo en forma de libro, puede que éstos te sirvan de ayuda:
La programación es un mundo que evoluciona a una velocidad de vértigo. Los autores de estos libros lo saben, por eso suelen encargarse de actualizar su contenido regularmente. Asegúrate de que así sea antes de adquirirlos
Participo en el programa de afiliados de Udemy y Amazon, lo que significa que, si compras alguno de estos cursos y/o libros, yo me llevaré una pequeña comisión y a ti no costará nada extra. Vamos, lo que se dice un win-win
Otros artículos que pueden interesarte