Angular 21 de Cero a Experto / Routing
Parámetros de ruta como señales
Una página de detalle necesita saber QUÉ mostrar: /gasto/5 muestra el gasto 5. Ese 5 es
un parámetro de ruta, y Angular te lo entrega. La forma clásica era suscribirse a un
Observable; la forma moderna —y hermosa— es recibir el parámetro directamente como un
input() señal del componente, sin suscripciones. Hoy dominas ambas y entiendes por qué la
nueva es un salto enorme.
El número de la habitación
Definir una ruta con parámetro
export const routes: Routes = [
{ path: '', component: ListaGastosComponent },
{ path: 'gasto/:id', component: DetalleGastoComponent }, // :id es un parámetro
];
// /gasto/5 → DetalleGastoComponent con id = "5"
// /gasto/8 → el mismo componente con id = "8"Y enlazas a una ruta con parámetro pasando los segmentos:
// enlace estático con el id interpolado
<a [routerLink]="['/gasto', g.id]">Ver {{ g.nombre }}</a>
// navegación por lógica
this.router.navigate(['/gasto', g.id]);La forma moderna: el parámetro como input() señal
Angular 21 puede entregarte los parámetros de la URL directamente como INPUTS del componente.
Activas la característica una vez y luego declaras un input() con el mismo nombre que el
parámetro:
import { provideRouter, withComponentInputBinding } from '@angular/router';
providers: [
provideRouter(routes, withComponentInputBinding()), // ← activa el binding
];import { Component, input, computed, inject } from '@angular/core';
import { BolsilloStore } from './bolsillo.store';
@Component({
selector: 'app-detalle-gasto',
template: `
@if (gasto()) {
<h2>{{ gasto()!.nombre }}</h2>
<p>{{ gasto()!.valor }} COP</p>
} @else {
<p>Gasto no encontrado.</p>
}
`,
})
export class DetalleGastoComponent {
private store = inject(BolsilloStore);
// el parámetro ':id' de la URL llega como este input señal (string)
id = input.required<string>();
// deriva el gasto del id: cuando la URL cambia, 'id' cambia y esto se recalcula solo
gasto = computed(() => this.store.gastos().find((g) => g.id === Number(this.id())));
}Esto es precioso: el parámetro de la URL es un input() señal, así que puedes DERIVAR de él
con computed. Navegas de /gasto/5 a /gasto/8, el id cambia, y gasto se recalcula
solo —el mismo componente, sin código de sincronización—.
Antes (subscribe a ActivatedRoute) vs Ahora (input signal)
Query params: configurar, no identificar
Los parámetros de ruta (:id) IDENTIFICAN un recurso; los query params (?categoria=comida)
lo CONFIGURAN (filtros, orden, página). Se leen de forma parecida:
// enlace con query params
<a [routerLink]="['/']" [queryParams]="{ categoria: 'comida', orden: 'valor' }">
Ver comida
</a>
// produce: /?categoria=comida&orden=valor
// leerlos (con el binding de inputs, un input del mismo nombre los recibe)
categoria = input<string>(); // ?categoria=comida → 'comida'Regla: si el dato define CUÁL recurso ves, es parámetro de ruta; si AJUSTA cómo lo ves, es query param. Filtros, búsqueda, orden y paginación → query params (así el estado del filtro vive en la URL y se puede compartir/marcar).
La lógica en vivo: derivar de un parámetro
El router no corre en el sandbox, pero la lógica —"del id de la URL, deriva el gasto"— es TS
puro y reactivo. Simula el id como una señal que cambia al "navegar":
¿Qué ventaja tiene recibir el parámetro `:id` como `input()` señal frente a suscribirse a `ActivatedRoute.paramMap`?
Mini-reto
En el playground: 1) añade más gastos con distintos id; 2) crea un computed
existe que sea true/false según si el gasto del idParam actual se encontró;
3) "navega" a varios ids con idParam.set(...) y observa cómo gasto y existe cambian
solos. Luego escribe cómo se vería el componente Angular equivalente con input.required<string>()
y su computed.
Qué sigue
Tus páginas ya reaccionan a la URL. Pero cargar TODO el código de todas las páginas al inicio es un desperdicio: el usuario quizá nunca abra las estadísticas. La próxima lección es el lazy loading —cargar cada página solo cuando se visita—, clave para que apps grandes arranquen rápido.