Parámetros en rutas con ActivatedRoute

Intermedio
Angular
Angular
Actualizado: 24/09/2025

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 - Autor del tutorial

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.