Listado de la etiqueta: standalone

Router Signals en Angular 21: navegación standalone sin cargar todo el Router

Siempre que necesitaba saber si una ruta estaba activa en Angular, inyectaba el Router completo y me suscribía a los eventos. En componentes simples de navegación, era matar moscas a cañonazos. Angular 21 introduce funciones standalone que devuelven Signals — cargás exactamente lo que necesitás, sin más.

El problema con inyectar el Router entero

// ❌ Antes: inyectar todo el Router para saber si una ruta está activa
import { Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs/operators';

@Component({ ... })
export class SidebarComponent implements OnInit, OnDestroy {
  isDashboard = false;
  private sub = Subscription.EMPTY;

  constructor(private router: Router) {}

  ngOnInit() {
    this.sub = this.router.events
      .pipe(filter(e => e instanceof NavigationEnd))
      .subscribe(() => {
        this.isDashboard = this.router.url === '/dashboard';
      });
  }

  ngOnDestroy() { this.sub.unsubscribe(); }
}

isActive(): la función standalone con Signal

// ✅ Angular 21: función standalone, devuelve Signal
import { Component } from '@angular/core';
import { isActive } from '@angular/router';
import { RouterLink } from '@angular/router';

@Component({
  selector: 'app-sidebar',
  standalone: true,
  imports: [RouterLink],
  template: `
    <nav>
      <a routerLink="/dashboard" [class.active]="isDashboard()">Dashboard</a>
      <a routerLink="/usuarios" [class.active]="isUsuarios()">Usuarios</a>
      <a routerLink="/reportes" [class.active]="isReportes()">Reportes</a>
    </nav>
  `
})
export class SidebarComponent {
  isDashboard = isActive('/dashboard', { exact: true });
  isUsuarios  = isActive('/usuarios');
  isReportes  = isActive('/reportes');
}

Otras funciones standalone del Router

import { isActive, currentRoute, routeParams, queryParams, routeData } from '@angular/router';

@Component({ ... })
export class DetalleComponent {
  // Parámetros de ruta como Signal — sin ActivatedRoute
  params     = routeParams();
  usuarioId  = computed(() => this.params()['id']);

  // Query params como Signal
  qParams = queryParams();
  pagina  = computed(() => Number(this.qParams()['page'] ?? 1));

  // Data estática de la ruta
  data   = routeData();
  titulo = computed(() => this.data()['titulo'] ?? 'Sin título');
}

Navigation API experimental (v21.0)

// app.config.ts — habilitar Navigation API experimental
import { provideExperimentalNavigationApi } from '@angular/common';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideExperimentalNavigationApi()
  ]
};

Location strategies: control sobre el trailing slash (v21.2)

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes, withRouterConfig({
      trailingSlash: 'never'  // 'always' | 'never' | 'preserve'
    }))
  ]
};

Por qué el tree-shaking importa

Con las funciones standalone, el bundler puede eliminar del bundle final todo el código del Router que no se usa. Si solo usás isActive y routeParams, el resto no viaja al usuario. En aplicaciones grandes esto puede reducir el bundle inicial de forma significativa — especialmente relevante en conexiones móviles lentas.


Artículo anterior: Vitest reemplaza a Karma y Angular habla con tu IA: el nuevo ecosistema de herramientas | Serie Angular 20 → 21.2 | Próximo: Guía práctica: cómo migré un proyecto de Angular 19/20 a Angular 21.2 sin morir en el intento →