← maurobernal.com.ar

Etiqueta: router

  • 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 →

Tags

tsql (27)mssql (26)sql (20)devops (20)dotnet (18)docker (15)performance (14)contenedores (11)dotnet10 (10)linux (9)csharp (8)microservicios (7)angular (7)angular21 (7)sql server (6)issabel (6)docker-compose (6)typescript (6)mysql (5).NET (5)