Routing en Angular: Guía completa: Parte 4

Retomamos esta guía sobre el routing después de dar los pasos básicos para crear rutas y adentrarnos brevemente en el mundo de los observablesSi te perdiste la parte anterior, aquí la tienes.

En esta 4ª parte vamos a aprender a trabajar con query paramsentre otras muchas cosas. ¡Vamos allá! 💪

Cómo pasarle query params y fragmentos a nuestras rutas

A parte de los parámetros de una ruta que hemos visto, tipo http://localhost:4200/users/8/john, existen otros parámetros que podemos obtener de la URL, como los query params.

Los "query params" son los parámetros de consulta que se utilizan para comunicarnos con una base de datos y solicitarle información.

🗣 Query significa "consulta".

Los query params se representan con un signo de interrogación para anunciar que a continuación viene una consulta, para después especificarle más detalles.

Se pueden unir varios query params con el signo &. Por ejemplo:

http://localhost:4200/users/8/john?mode=editing&[otro query param aquí].

🤓 Vamos a aprender a pasar query params a nuestra URL y obtenerlos de la misma URL.

También veremos cómo añadir fragmentos y obtenerlos de la URL, que sirven para ir a una parte específica de una página. Se representan con el símbolo de la almohadilla #. Por ejemplo:

http://localhost:4200/users/8/john?mode=editing#loading.

Vamos a aprender a hacer todo esto de dos maneras: 

  • con la directiva routerLink
  • de manera programática, con el método navigate

Con la directiva routerLink

1. Añadimos una ruta más a nuestro app.module.ts que nos permita añadir un servidor, así que le añadimos un id como segmento dinámico + la parte edit, describiendo así lo que queremos que suceda en esa ruta. El componente vinculado a esa ruta será el EditServerComponent.

  { path: 'servers/:id/edit', component: EditServerComponent }

2. En el template del ServersComponent editamos la lista que muestra los servidores (el <a>).

Haremos lo mismo más adelante en el UsersComponent.

3. Añadimos la propiedad routerLink y la vinculamos con property binding a un array con varios segmentos. El primero será un absolute path a la ruta /servers. El segundo, un id estático (el que queramos) y el tercero, el segmento edit.


      <a href="#" class="list-group-item list-group-item-action" *ngFor="let server of servers"
        [routerLink]="['/servers', 3, 'edit']">

Si guardas y haces click en cualquiera de los nombres de la lista de servidores (Productionserver, Testserver o Devserver), verás que se muestra la URL http://localhost:4200/servers/3/edit. 👍

Supongamos que ahora queremos añadir un query parameter para decidir si un servidor puede ser modificado o no (en inglés, edit). 

Sería incorrecto añadirlo como un fragmento más del array, tipo:


      <a href="#" class="list-group-item list-group-item-action" *ngFor="let server of servers"
        [routerLink]="['/servers', 3, 'edit', 'q=']">

En lugar de eso, debemos usar una propiedad del routerLink que también es susceptible de ser vinculada con property binding: la propiedad queryParamsque espera un objecto de JavaScript (en adelante, JS), donde definiremos key-value pairs de los parámetros que queremos añadirPor ejemplo:

  • key = allowEdit
  • value = 1

      <a href="#" class="list-group-item list-group-item-action" *ngFor="let server of servers"
        [routerLink]="['/servers', 3, 'edit']" [queryParams]="{allowEdit: '1'}">

Verás que ahora al hacer click en cualquier servidor de la lista, la URL se actualiza a http://localhost:4200/servers/3/edit?allowEdit=1.

Puedes probar también a añadir otro key-value pair, y verás como se añade a la query de la URL, separada por el símbolo &,


      <a href="#" class="list-group-item list-group-item-action" *ngFor="let server of servers"
        [routerLink]="['/servers', 3, 'edit']" [queryParams]="{allowEdit: '1', allowDelete: '2'}">

mostrando la URL http://localhost:4200/servers/3/edit?allowEdit=1&allowDelete=2

Yo lo voy a dejar como estaba antes:


      <a href="#" class="list-group-item list-group-item-action" *ngFor="let server of servers"
        [routerLink]="['/servers', 3, 'edit']" [queryParams]="{allowEdit: '1'}">

También tenemos la propiedad fragment,  que espera un string. Tiene dos modalidades:

  • modo con property binding: [fragment]="'loading'"
  • modo sin property binding: fragment="loading"

Por simplicidad, a mí me gusta más sin property binding, así que así lo añado.


      <a href="#" class="list-group-item list-group-item-action" *ngFor="let server of servers"
        [routerLink]="['/servers', 3, 'edit']" [queryParams]="{allowEdit: '1'}"
        fragment="loading">

Verás que ahora cualquier URL al hacer click en un servidor de la lista se muestra así: http://localhost:4200/servers/3/edit?allowEdit=1#loading.

De manera programática

Veamos cómo hacerlo ahora de manera programática. 👩‍💻

1. Supongamos que queremos cargar únicamente un servidor, así que en la home page, en lugar de tener un botón que diga Load servers, vamos a cambiarlo por uno que diga Load server nº8, por ejemplo.

2. Cambiamos también el nombre del método onLoadServers por onLoadServer y le pasamos por parámetro el número del servidor ficticio.


<button class="btn btn-dark" (click)="onLoadServer(8)">Load server nº8</button>

3. Ajustamos el método en el home.component.ts, que espera un id como argumento.

4. En el método navigate, dejamos el primer segmento como está y añadimos como segundo segmento el id del parámetro y como último segmento, edit.

5. Le añadimos query params como segundo argumento del método navigateen modo de objeto de JS (en inglés, JS object). Al igual que antes, query params es un JS object que debe escribirse como key-value pairs. 

6. Le añadimos un key-value pairpor ejemplo, allowEdit: 8.

El método navigate también acepta fragments dentro del segundo argumento, así que le pasamos uno, por ejemplo, loading, para utilizar el mismo ejemplo  de arriba.


  onLoadServer(id: number) {
    // complex code that connects to a backend

    // navigation to Servers page
    this.router.navigate(['/servers', id, 'edit'], { queryParams: { allowEdit: '8' }, fragment: 'loading' });
  }

Aquí un esquemita para entendernos mejor:

navigate (array['segmento1', 'segmento2'], {queryParams: { key: 'value' }, fragment: 'value'})

Si guardamos y vamos a nuestro navegador, al hacer click sobre Load server nº8 verás que tu URL cambia a http://localhost:4200/servers/8/edit?allowEdit=8#loading. ¡Genial! 👌

Cómo obtener query params y fragmentos de una ruta

Con esto ya hemos aprendido a añadir más datos a nuestras rutas. Veamos ahora cómo obtener dichos datos.

El lugar donde tiene más sentido que obtengamos los query params y fragmentos es en el EditServerComponent. Como resumen, podríamos decir que el archivo edit-server.component.ts está usando el servicio Servers para obtener el server con el ID 1 y poder actualizarlo.

1. Para poder acceder a los query params y a los fragmentos, necesitamos inyectar la ActivatedRoute. La convertimos en propiedad y la llamamos route. 

2. En el ngOnInit, obtenemos los query params y los fragmentos. Al igual que antes, tenemos dos formas de obtenerlos. Hacemos un console.log para ver mejor cómo funcionan en la consola.

Primera: accedemos al snapshot y desde ahí a los query params. Hacemos lo mismo con el fragment.

  constructor(private serversService: ServersService,
              private route: ActivatedRoute) { }

  ngOnInit() {
    console.log(this.route.snapshot.queryParams);
    console.log(this.route.snapshot.fragment);

Esta manera conlleva el mismo problema del que hemos hablado en parte #3, ya que el código del ngOnInit se ejecutará únicamente en el momento de instanciar el componente. Así que si desde nuestra UI cambiásemos los params, nuestro código no se sincronizaría bien, porque no sería un código reactivo. 🙆‍♀️

Si guardas y vas a tu navegador, la URL debería seguir siendo http://localhost:4200/servers/8/edit?allowEdit=8#loading, pero ahora en la consola se muestra:

|   Elements    Console    Sources    Performance    Network    ...

{allowEdit: "1"}
  allowEdit: "1"
  __proto__: Object
loading

Segunda: Esta es la alternativa más conveniente, usando los observables "queryParams" y "fragment" y suscribiéndonos a ellos. Así nuestro código será reactivo y podrá estar al tanto cuando suceda algún cambio en los parámetros de la URL. 👀

Recuerda que no necesitamos desuscribirnos porque Angular ya lo hace por nosotros. 🤙

this.route.queryParams.subscribe();
this.route.fragment.subscribe();

Cómo mostrar una página de "perfil" por cada servidor

Con todo lo aprendido, vamos a mejorar un pelín nuestra app. En mi opinión, esta es la mejor forma de aprender a programar: crear una app que haga pocas cosas (o nada 😅) e ir añadiendo mejoras poco a poco, en lugar de querer construir una app con funcionalidades completas desde el principio. Es el método que aprendí de profesores tan buenos como Max, en cursos como el suyo de Angular.

Vamos a centrarnos en mostrar una página de "perfil" por cada servidor al hacer click en uno de ellos desde la lista de servidores:

lista de servers

1. En el users.component.html, añadimos un routerLink a la lista de servidores, vinculado a un array con los siguientes segmentos:

  • "/users"
  • el ID dinámico obtenido a partir de la variable local user del ngFor.
  • el nombre del usuario obtenido de manera dinámica desde la variable local user del ngFor.

      <a href="#" class="list-group-item list-group-item-action" *ngFor="let user of users"
        [routerLink]="['/users', user.id, user.name]">

Porque en su momento creamos un path en el app.module.ts que coincide con la estructura de ese array.

Hagamos lo mismo en el servers.component.html. Ahí sustituimos el ID estático por uno dinámico y eliminamos el segmento edit.


     <a href="#" class="list-group-item list-group-item-action" *ngFor="let server of servers"
        [routerLink]="['/servers', server.id]" [queryParams]="{allowEdit: '1'}" fragment="loading">

Para esta URL aún no hemos creado su ruta pertinente en el app.module.ts, así que ese es nuestro siguiente paso. Como queremos mostrar un único servidor en esa ruta, le pasamos el componente Server.

  { path: 'servers/:id', component: ServerComponent },

Para poder mostrar el servidor correcto en cada caso, según hagamos click en uno de los servidores de la lista, debemos primero extraer los datos correspondientes de la URL. Para eso debemos trabajar en el server.component.ts.

2. Inyectamos el ActivatedRoute y lo convertimos en propiedad de TypeScript (en adelante, TS), a la que llamamos route.

3. En el ngOnInit, obtenemos el ID a través de la propiedad params de la snapshot y lo guardamos en una variable. De esta manera podemos usar esa variable para sustituir el valor estático que teníamos (1).

Para poder reaccionar a cambios que sucedan una vez nuestro componente se instancie, nos suscribimos usando el observable "params" de la route.

El método subscribe espera un parámetro como argumento, que será de tipo Params. En el cuerpo de la función es donde actualizamos el ID del server para obtener el servidor correcto en todo momento aunque los parámetros de la URL cambien, porque ahora Angular ya está avisado. 👮‍♀️

  constructor(private serversService: ServersService, private route: ActivatedRoute) { }

  ngOnInit() {
    const id = this.route.snapshot.params['id'];
    this.server = this.serversService.getServer(id);

    this.route.params.subscribe((params: Params) => {
      this.server = this.serversService.getServer(params['id']);
    });

Si guardas y en tu app haces click en Servers, verás que la consola de las dev tools nos devuelve un error. La razón es que en nuestro servers.component.html estamos cargando el componente Server (<app-server>), por tanto Angular lo cargará aunque no tenga disponible ningún ID de un servidor para cargar 🙈. Y de ahí el error. Así que la comentamos de momento.


    <!-- <app-server></app-server> -->

Errores comunes de conversión de tipos

Sin embargo, al guardar nuestro código la consola nos sigue devolviendo un error. Sinceramente, el mensaje de error no es muy intuitivo 😒. Menos mal que está Max con su curso de Angular para aclarar en profundidad qué está sucediendo. Voy a tratar de resumirlo: Lo que ocurre aquí es un tema de conversión de tipos

Siempre que obtenemos un parámetro de una URL, éste vendrá en formato de "string", porque una URL tiene ese formato de texto.

Por tanto, cuando creamos la variable id, no estamos obteniendo un número, sino un string.

    const id = this.route.snapshot.params['id']; // así es un string ('1'), no un number (1)

Así que Angular, con este código, intenta buscar un server con un ID de tipo string, pero nosotros hemos usando números para los IDs, como se ve en el ServersService. 🙄

Lo único que tenemos que hacer es convertir el string en number, sencillamente añadiendo el signo + delante del valor de la variable id, y también cuando lo actualicemos en el método subscribe. 😉

// const id = this.route.snapshot.params['id']; // así es un string ('1'), no un number (1)
    const id = +this.route.snapshot.params['id'];
    this.server = this.serversService.getServer(id);

    this.route.params.subscribe((params: Params) => {
      this.server = this.serversService.getServer(+params['id']);
    });

¡Y ya está! Ahora si pinchas en cualquier servidor de la lista, nos lleva a su "perfil".

server profile

¡FIN DE LA PARTE #4!

Y hasta aquí esta 4ª parte. En la siguiente entrega (disponible próximamente) veremos cómo crear nested routes para configurar un estructura de rutas hijas (child routes), entre otras cosas. Por el momento, espero que hayas aprendido algo nuevo 😊.  Si te queda alguna duda, ¡nos vemos en los comentarios!

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

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