Parámetros de ruta
Los parámetros de ruta permiten crear URLs dinámicas que transportan información específica entre páginas de nuestra aplicación. Esta funcionalidad es esencial para construir páginas como detalles de producto, perfiles de usuario o cualquier vista que necesite mostrar información basada en un identificador único.
Configuración de rutas con parámetros
Para definir un parámetro en una ruta, utilizamos la sintaxis de dos puntos seguida del nombre del parámetro. Esta configuración se realiza en el archivo de rutas de la aplicación:
// app.routes.ts
import { Routes } from '@angular/router';
import { ProductListComponent } from './product-list.component';
import { ProductDetailComponent } from './product-detail.component';
export const routes: Routes = [
{ path: '', component: ProductListComponent },
{ path: 'products', component: ProductListComponent },
{ path: 'products/:id', component: ProductDetailComponent },
{ path: '**', redirectTo: '' }
];
En este ejemplo, :id
es un parámetro de ruta que puede contener cualquier valor. Cuando el usuario navegue a /products/123
, el valor 123
estará disponible como parámetro id
en el componente ProductDetailComponent
.
Lectura de parámetros con ActivatedRoute
Para acceder a los parámetros de ruta dentro de un componente, utilizamos el servicio ActivatedRoute. En Angular 20, podemos inyectarlo tanto por constructor como mediante la función inject()
:
// product-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { inject } from '@angular/core';
@Component({
selector: 'app-product-detail',
standalone: true,
template: `
<h2>Detalle del Producto</h2>
@if (productId) {
<p>ID del producto: {{ productId }}</p>
<p>Cargando información del producto...</p>
}
`
})
export class ProductDetailComponent implements OnInit {
private route = inject(ActivatedRoute);
productId: string | null = null;
ngOnInit() {
// Obtener el parámetro usando snapshot (valor estático)
this.productId = this.route.snapshot.params['id'];
}
}
Valores estáticos vs dinámicos
Angular ofrece dos formas principales de leer parámetros de ruta: mediante snapshot para valores estáticos y mediante observables para valores dinámicos.
Usando snapshot para valores que no cambian durante el ciclo de vida del componente:
ngOnInit() {
const productId = this.route.snapshot.params['id'];
this.loadProduct(productId);
}
Usando observables cuando los parámetros pueden cambiar sin que el componente se destruya:
ngOnInit() {
this.route.params.subscribe(params => {
const productId = params['id'];
this.loadProduct(productId);
});
}
El enfoque con observables es útil cuando navegamos entre rutas del mismo componente, por ejemplo, de /products/1
a /products/2
sin que Angular destruya y recree el componente.
Navegación con parámetros usando routerLink
Para navegar a rutas con parámetros desde el template, utilizamos routerLink
con un array que incluye la ruta base y los parámetros:
// product-list.component.ts
@Component({
selector: 'app-product-list',
standalone: true,
imports: [RouterLink],
template: `
<h2>Lista de Productos</h2>
<div class="product-grid">
@for (product of products; track product.id) {
<div class="product-card">
<h3>{{ product.name }}</h3>
<p>Precio: {{ product.price | currency }}</p>
<a [routerLink]="['/products', product.id]" class="btn-detail">
Ver Detalles
</a>
</div>
}
</div>
`
})
export class ProductListComponent {
products = [
{ id: 1, name: 'Laptop Gaming', price: 1299.99 },
{ id: 2, name: 'Smartphone Pro', price: 899.99 },
{ id: 3, name: 'Tablet Ultra', price: 599.99 }
];
}
Múltiples parámetros de ruta
Las rutas pueden incluir múltiples parámetros para casos más complejos:
// Configuración de ruta con múltiples parámetros
{ path: 'categories/:categoryId/products/:productId', component: ProductDetailComponent }
Para acceder a múltiples parámetros:
ngOnInit() {
const categoryId = this.route.snapshot.params['categoryId'];
const productId = this.route.snapshot.params['productId'];
console.log('Categoría:', categoryId);
console.log('Producto:', productId);
}
Y para navegar con múltiples parámetros:
// En el template
<a [routerLink]="['/categories', category.id, 'products', product.id]">
{{ product.name }}
</a>
Ejemplo práctico completo
Veamos un ejemplo completo de una aplicación que lista usuarios y muestra sus detalles:
// user-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { inject } from '@angular/core';
interface User {
id: number;
name: string;
email: string;
department: string;
}
@Component({
selector: 'app-user-detail',
standalone: true,
template: `
<div class="user-detail">
@if (user) {
<h2>{{ user.name }}</h2>
<p><strong>Email:</strong> {{ user.email }}</p>
<p><strong>Departamento:</strong> {{ user.department }}</p>
<button (click)="goBack()">Volver a la lista</button>
} @else {
<p>Usuario no encontrado</p>
}
</div>
`,
styles: [`
.user-detail {
max-width: 500px;
margin: 20px auto;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
}
`]
})
export class UserDetailComponent implements OnInit {
private route = inject(ActivatedRoute);
private router = inject(Router);
user: User | null = null;
// Simulamos una base de datos de usuarios
private users: User[] = [
{ id: 1, name: 'Ana García', email: 'ana@empresa.com', department: 'Desarrollo' },
{ id: 2, name: 'Carlos López', email: 'carlos@empresa.com', department: 'Diseño' },
{ id: 3, name: 'María Torres', email: 'maria@empresa.com', department: 'Marketing' }
];
ngOnInit() {
const userId = Number(this.route.snapshot.params['id']);
this.user = this.users.find(u => u.id === userId) || null;
}
goBack() {
this.router.navigate(['/users']);
}
}
Los parámetros de ruta proporcionan una forma elegante y tipada de pasar información entre componentes mediante la URL, manteniendo la aplicación navegable y permitiendo que los usuarios compartan enlaces específicos.
Query params
Los query params son parámetros opcionales que se añaden al final de una URL después del signo de interrogación ?
. A diferencia de los parámetros de ruta que forman parte del path, los query params proporcionan información adicional sin afectar la estructura de la ruta y son especialmente útiles para funcionalidades como filtros, paginación o configuraciones de vista.
Diferencias entre params y queryParams
Mientras que los parámetros de ruta son obligatorios y forman parte del path de la URL, los query params son opcionales y se añaden como información complementaria:
// Parámetro de ruta (obligatorio)
/products/123
// Query params (opcionales)
/products?category=electronics&sort=price&page=2
// Combinados
/products/123?color=red&size=large
Los query params mantienen la misma ruta base pero permiten modificar el comportamiento o filtrado de la vista sin cambiar el componente que se carga.
Lectura de query params con ActivatedRoute
Para acceder a los query params, utilizamos la propiedad queryParams
de ActivatedRoute:
// product-list.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { inject } from '@angular/core';
@Component({
selector: 'app-product-list',
standalone: true,
template: `
<div class="filters">
<p>Categoría: {{ currentCategory || 'Todas' }}</p>
<p>Ordenar por: {{ currentSort || 'Nombre' }}</p>
<p>Página: {{ currentPage || 1 }}</p>
</div>
<div class="products">
@for (product of filteredProducts; track product.id) {
<div class="product-card">
<h3>{{ product.name }}</h3>
<p>{{ product.category }}</p>
<p>{{ product.price | currency }}</p>
</div>
}
</div>
`
})
export class ProductListComponent implements OnInit {
private route = inject(ActivatedRoute);
currentCategory: string | null = null;
currentSort: string | null = null;
currentPage: string | null = null;
filteredProducts: any[] = [];
products = [
{ id: 1, name: 'Laptop', category: 'electronics', price: 999 },
{ id: 2, name: 'Mesa', category: 'furniture', price: 299 },
{ id: 3, name: 'Smartphone', category: 'electronics', price: 699 }
];
ngOnInit() {
// Leer query params con snapshot (valor actual)
this.currentCategory = this.route.snapshot.queryParams['category'];
this.currentSort = this.route.snapshot.queryParams['sort'];
this.currentPage = this.route.snapshot.queryParams['page'];
this.applyFilters();
}
private applyFilters() {
this.filteredProducts = this.products.filter(product => {
if (this.currentCategory && product.category !== this.currentCategory) {
return false;
}
return true;
});
}
}
Query params dinámicos con observables
Cuando los query params pueden cambiar durante el ciclo de vida del componente, utilizamos el observable queryParams
:
ngOnInit() {
// Suscribirse a cambios en query params
this.route.queryParams.subscribe(params => {
this.currentCategory = params['category'];
this.currentSort = params['sort'];
this.currentPage = params['page'] || '1';
this.applyFilters();
this.loadProducts();
});
}
private loadProducts() {
// Simular carga de productos basada en query params
console.log('Cargando productos con filtros:', {
category: this.currentCategory,
sort: this.currentSort,
page: this.currentPage
});
}
Navegación con query params
Para navegar incluyendo query params, utilizamos la propiedad queryParams
en routerLink
:
// filter-menu.component.ts
@Component({
selector: 'app-filter-menu',
standalone: true,
imports: [RouterLink],
template: `
<nav class="filter-menu">
<h3>Filtros</h3>
<div class="filter-group">
<h4>Categoría</h4>
<a [routerLink]="['/products']" [queryParams]="{}">Todas</a>
<a [routerLink]="['/products']" [queryParams]="{category: 'electronics'}">
Electrónicos
</a>
<a [routerLink]="['/products']" [queryParams]="{category: 'furniture'}">
Muebles
</a>
</div>
<div class="filter-group">
<h4>Ordenar por</h4>
<a [routerLink]="['/products']"
[queryParams]="{sort: 'name'}"
[queryParamsHandling]="'merge'">
Nombre
</a>
<a [routerLink]="['/products']"
[queryParams]="{sort: 'price'}"
[queryParamsHandling]="'merge'">
Precio
</a>
</div>
</nav>
`
})
export class FilterMenuComponent {}
Manejo de query params con queryParamsHandling
La propiedad queryParamsHandling
controla cómo se combinan los query params existentes con los nuevos:
- 'merge': Combina los query params nuevos con los existentes
- 'preserve': Mantiene todos los query params existentes
- '' (por defecto): Reemplaza todos los query params
// Ejemplos de queryParamsHandling
<a [routerLink]="['/products']"
[queryParams]="{page: 2}"
[queryParamsHandling]="'merge'">
Página 2 (mantiene filtros existentes)
</a>
<a [routerLink]="['/products']"
[queryParams]="{category: 'electronics'}"
[queryParamsHandling]="'preserve'">
Electrónicos (preserva todos los params)
</a>
Navegación programática con query params
También podemos navegar programáticamente usando el servicio Router:
import { Router } from '@angular/router';
@Component({
selector: 'app-search',
standalone: true,
template: `
<input #searchInput (keyup.enter)="search(searchInput.value)"
placeholder="Buscar productos...">
<button (click)="search(searchInput.value)">Buscar</button>
<button (click)="clearFilters()">Limpiar filtros</button>
`
})
export class SearchComponent {
private router = inject(Router);
search(query: string) {
// Navegar con query params
this.router.navigate(['/products'], {
queryParams: { search: query, page: 1 },
queryParamsHandling: 'merge'
});
}
clearFilters() {
// Navegar sin query params
this.router.navigate(['/products']);
}
}
Ejemplo práctico: Sistema de filtros completo
Veamos un ejemplo completo que combina múltiples query params para crear un sistema de filtros:
// product-search.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { inject } from '@angular/core';
import { Subscription } from 'rxjs';
interface ProductFilter {
category?: string;
minPrice?: number;
maxPrice?: number;
search?: string;
page?: number;
sort?: string;
}
@Component({
selector: 'app-product-search',
standalone: true,
template: `
<div class="search-container">
<div class="current-filters">
<h3>Filtros activos:</h3>
@if (filters.category) {
<span class="filter-tag">
Categoría: {{ filters.category }}
<button (click)="removeFilter('category')">×</button>
</span>
}
@if (filters.search) {
<span class="filter-tag">
Búsqueda: {{ filters.search }}
<button (click)="removeFilter('search')">×</button>
</span>
}
@if (filters.minPrice || filters.maxPrice) {
<span class="filter-tag">
Precio: {{ filters.minPrice || 0 }} - {{ filters.maxPrice || '∞' }}
<button (click)="removePriceFilter()">×</button>
</span>
}
</div>
<div class="results-info">
<p>Página {{ filters.page || 1 }} - {{ totalResults }} productos encontrados</p>
</div>
</div>
`,
styles: [`
.filter-tag {
display: inline-block;
background: #e3f2fd;
padding: 4px 8px;
margin: 2px;
border-radius: 4px;
font-size: 12px;
}
.filter-tag button {
margin-left: 4px;
background: none;
border: none;
cursor: pointer;
font-weight: bold;
}
`]
})
export class ProductSearchComponent implements OnInit, OnDestroy {
private route = inject(ActivatedRoute);
private router = inject(Router);
private subscription = new Subscription();
filters: ProductFilter = {};
totalResults = 0;
ngOnInit() {
// Suscribirse a cambios en query params
this.subscription.add(
this.route.queryParams.subscribe(params => {
this.filters = {
category: params['category'],
minPrice: params['minPrice'] ? Number(params['minPrice']) : undefined,
maxPrice: params['maxPrice'] ? Number(params['maxPrice']) : undefined,
search: params['search'],
page: params['page'] ? Number(params['page']) : 1,
sort: params['sort'] || 'name'
};
this.loadResults();
})
);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
removeFilter(filterName: keyof ProductFilter) {
const currentParams = { ...this.route.snapshot.queryParams };
delete currentParams[filterName];
this.router.navigate(['/products'], {
queryParams: currentParams
});
}
removePriceFilter() {
const currentParams = { ...this.route.snapshot.queryParams };
delete currentParams['minPrice'];
delete currentParams['maxPrice'];
this.router.navigate(['/products'], {
queryParams: currentParams
});
}
private loadResults() {
// Simular carga de resultados basada en filtros
console.log('Aplicando filtros:', this.filters);
this.totalResults = Math.floor(Math.random() * 100) + 1;
}
}
Los query params ofrecen una forma flexible y persistente de manejar estado de filtros, búsquedas y configuraciones en la URL, permitiendo que los usuarios compartan enlaces específicos y mantengan el estado al recargar la página.
Fuentes y referencias
Documentación oficial y recursos externos para profundizar en Angular
Documentación oficial de Angular
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Angular es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Angular
Explora más contenido relacionado con Angular y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
- Comprender qué son los parámetros de ruta y cómo configurarlos en Angular.
- Aprender a leer parámetros de ruta y query params usando ActivatedRoute.
- Diferenciar entre valores estáticos y dinámicos de parámetros mediante snapshot y observables.
- Implementar navegación con parámetros y query params en plantillas y de forma programática.
- Gestionar múltiples parámetros y sistemas de filtros usando queryParams y queryParamsHandling.