routing guia completa portada

Routing en Angular: Guía completa: Parte 2

Última actualización:

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 routerLinkActiveque 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>.


        <li class="nav-item"><a class="nav-link" routerLinkActive="active" routerLink="/">Home</a></li>
        <li class="nav-item"><a class="nav-link" routerLinkActive="active" routerLink="/servers">Servers</a></li>
        <li class="nav-item"><a class="nav-link" routerLinkActive="active" [routerLink]="['/users']">Users</a></li>

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:


     <a class="nav-link" routerLinkActive="active" routerLink="/">Home</a>

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 routerLinkActiveOptionsque debemos vincularla con property bindingporque 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/
👉
100% match 😍
(match = coincidencia)
  • 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 😍


          <a class="nav-link" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}" routerLink="/">
            Home
          </a>


¡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 al un método llamado, por ejemplo, onLoadServers.


<button class="btn btn-dark" (click)="onLoadServers()">Load servers</button>

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 navigateque acepta un argumento para permitirnos navegar hasta una ruta nuevaEn 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:


 <a class="nav-link" routerLinkActive="active" [routerLink]="['/users']">Users</a>


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.


    <button class="btn btn-info" (click)="onReload()">Reload Servers</button>


2. Vamos al servers.component.ts e inyectamos el Router en el constructor, creando una propiedad (router) para poder usarlo.

3. En el método onReload, usamos 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. 🤔Este es el comportamiento esperado, ya que lo que le estamos pidiendo a Angular es que nos lleve al path http://localhost:4200/servers, path en el que ya nos encontramos.

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 ðŸ¤¨. Puede resultar extraño, ya que cuando hicimos este experimento con el routerLink, nos daba error, ya que Angular intentaba ir a la ruta inexistente http://localhost:4200/servers/servers. Pero parece que ahora no está haciendo eso, sino que nos "lleva" a http://localhost:4200/servers, que a efectos prácticos, significa que nos deja en la misma ruta en la que estamos actualmente.

La razón por la que está 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 relativeToque 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 ActivatedRouteque debe ser importado desde el angular/routerPara usarla, le damos un nombre, por ejemplo route, y la convertimos en una propiedad de TS.

👉 El ActivatedRoute es un objeto complejo de JS que almacena metadatos sobre la ruta que esté actualmente activa.

  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 😅. Pero éste es el comportamiento que esperábamos, y dado que no queremos romper nuestra app, simplemente comentamos el código y lo dejamos ahí de referencia.

    // 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

 
{ path: 'nombreDelPath/:nombreDelSegmento'}

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 😊.  Si te queda alguna duda, ¡nos vemos en los comentarios! Y si quieres seguir aprendiendo, nos vemos en la parte #3 (disponible próximamente)

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í. 

Si te gusta el blog, puedes darme tu apoyo por lo que vale un café ツ

¡Gracias!¡Gracias!

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

Días del 353 al 386
Objetivos versus realidad Y nuevamente, llegó otro día clave. Llegó…y pasó. El pasado 4 de marzo este Reto Computer Geek[...]
Construye Minioland: Tu primera aplicación con Angular | Parte #1
¿Qué vamos a construir?Minioland, o así he decidido llamar a esta sencilla app, totalmente responsive y de tipo Single Page[...]
Angular: Entendiendo la Directiva ngModel
Angular es un framework que nos permite, entre otras cosas, añadir contenido dinámico a nuestros archivos HTML. Una de las formas[...]
Si crees que este post puede serle útil a alguien, por favor, ¡compártelo!:

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Como toda web legal que se precie, utilizamos cookies para asegurar que damos la mejor experiencia al usuario en nuestro sitio web. Si continúas utilizando este sitio asumiremos que estás de acuerdo. más información

Los ajustes de cookies de esta web están configurados para "permitir cookies" y así ofrecerte la mejor experiencia de navegación posible. Si sigues utilizando esta web sin cambiar tus ajustes de cookies o haces clic en "Aceptar" estarás dando tu consentimiento a esto.

Cerrar