Agregar parámetros al navegar en Angular
Tags: angular, tutorial, routing, spanish
Published: 2020-11-24 12:30:17

Existen ocasiones en donde necesitamos pasar un parámetro, generalmente un identificador, a una ruta con la intención de utilizarlo para llamar a una API y obtener cierta información. En esta ocasión vamos a ver cómo definir una ruta que acepta un parámetro, cómo utilizar la directiva `routerLink` para pasar el valor del parámetro a través de la ruta y también cómo acceder al valor del parámetro desde un componente.

### Crear un nuevo componente

En primer lugar vamos a crear un nuevo componente ejecutando el siguiente comando del Angular CLI:

```powershell
ng generate component post
```
### Definir la nueva ruta
Lo próximo a realizar es agregar una nueva ruta hacia nuestro nuevo componente en el array de definiciones de rutas dentro de `app.module.ts`. Vamos a crear un nuevo objeto y esta vez en la propiedad `path` necesitaremos agregar `/:id` para indicar que la ruta espera recibir un parámetro llamado `id`. La definición de las rutas quedará de la siguiente forma:

```typescript
// src/app/app.module.ts
const routes: Route[] = [
  { path: "home", component: HomeComponent },
  { path: "posts", component: PostsComponent },
  { path: "posts/:id", component: PostComponent },
  { path: "**", redirectTo: "home" }
];
```

Si ejecutamos el comando `ng serve` podremos navegar a nuestro nuevo componente modificando la URL de nuestro navegador a, por ejemplo, `/posts/1`. De esta forma, el valor del parámetro `id` será el que pasemos al final de la URL.

### Enlazar con nuestra aplicación

Para navegar hacia el nuevo componente desde nuestra aplicación haremos uso de la directiva `routerLink` y agregaremos algunos valores de parámetros para simular un listado de links de navegación dentro de nuestro `PostsComponent`.

```html
<!-- src/app/posts/posts.component.html -->
<ul>
  <li><a [routerLink]="['/posts', 1]">Post 1</a></li>
  <li><a [routerLink]="['/posts', 2]">Post 2</a></li>
  <li><a [routerLink]="['/posts', 3]">Post 3</a></li>
</ul>
```

De esta manera le indicamos a Angular que queremos navegar a las rutas `/posts/1`, `/posts/2` o `/posts/3` al hacer click en alguno de los links.

### Obtener el valor del parámetro

Como hablamos al principio, es posible que querramos obtener cierta información utilizando el valor del parámetro `id` por lo que vamos a analizar las alternativas que tenemos para lograrlo.

Hay dos maneras diferentes de obtener el valor del parámetro.

La primera es a través del _snapshot_ de la ruta. El snapshot de la ruta nos provee de un objeto llamado [paramMap](https://angular.io/api/router/ParamMap) que expone los métodos `get`, `getAll` y `has` para interactuar con los parámetros de la ruta actual.

Para acceder al snapshot de la ruta es necesario inyectar en el componente la clase [ActivatedRoute](https://angular.io/api/router/ActivatedRoute) como se muestra a continuación:

```typescript
// src/app/post/post.component.ts
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";

@Component({
  selector: "app-post",
  templateUrl: "./post.component.html",
  styleUrls: ["./post.component.css"]
})
export class PostComponent implements OnInit {
  id: string;

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    this.id = this.route.snapshot.paramMap.get("id");
  }
}
```

Y, para simplificar el ejemplo, vamos a simplemente mostrar el valor en el html:

```html
<!-- src/app/post/post.component.html -->
<p>
  Post con id: {{ id }}
</p>
```

Haciendo click sobre cada link observaremos que la aplicación muestra correctamente el valor del parámetro. Sin embargo, esta alternativa para obtener el valor del identificador tiene una falencia ya que si la navegación a otros post se produce dentro de nuestro `PostCompoent`, éste no detecta el cambio en el `paramMap` por lo que no realiza la navegación.

![error-gif](https://dev-to-uploads.s3.amazonaws.com/i/mvqfzf9345ekcdvurl0a.gif) 

### Suscribir a los cambios en paramMap

Para solucionar el inconveniente antes mencionado debemos reemplazar el uso del snapshot de la ruta por una suscipción al parámetro `paramMap` del `ActivatedRoute` inyectado. De esta forma estaremos observando los cambios que suceden sobre los parámetros de la ruta y reaccionando correctamente ante cada uno de ellos.

El componente pasaría verse de la siguiente forma:

```typescript
// src/app/post/post.component.ts
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, ParamMap } from "@angular/router";

@Component({
  selector: "app-post",
  templateUrl: "./post.component.html",
  styleUrls: ["./post.component.css"]
})
export class PostComponent implements OnInit {
  id: string;

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    this.route.paramMap.subscribe((params: ParamMap) => {
      this.id = params.get('id');
    });
  }
}
```  

Lo más importante a recordar a la hora de decidir entre cuáles de los métodos utilizar es saber en dónde sucede el cambio del parámetro. 

Si el valor del parámetro es modificado dentro del mismo componente que lo utiliza entonces debemos suscribirnos al `paramMap`. 

En caso de que este no sea alterado dentro del componente podremos utilizar el snapshot de la ruta sin inconvenientes.

Aquí se puede ver el código final:

[Stackblitz](https://stackblitz.com/edit/angular-parametros-en-rutas?file=src/app/post/post.component.ts)