CodeForge

Angular 21 de Cero a Experto / Routing

Guards funcionales: el portero de cada ruta

Teoría22 min20 XP

No todas las páginas son para todos. El panel de admin exige estar logueado; una página de edición quizá deba avisarte si sales con cambios sin guardar. Los GUARDS son porteros que Angular consulta antes de activar (o dejar) una ruta: devuelven true para dejar pasar, false para bloquear, o una redirección. En Angular moderno son simples FUNCIONES —gracias a inject()—, mucho más ligeras que las clases que reemplazaron. Hoy los dominas.

El portero VIP

Un guard funcional CanActivateFn

Un guard de activación es una función que devuelve true (pasa), false (bloquea) o un UrlTree (redirige). Como corre en contexto de inyección, usa inject() para consultar lo que necesite:

auth.guard.ts
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from './auth.service';

export const authGuard: CanActivateFn = (route, state) => {
const auth = inject(AuthService);
const router = inject(Router);

if (auth.estaLogueado()) {
  return true;                              // pulsera OK → pasa
}
// sin sesión → redirige al login, recordando a dónde quería ir
return router.createUrlTree(['/login'], {
  queryParams: { volverA: state.url },
});
};

Y lo enchufas a la ruta con canActivate:

app.routes.ts
export const routes: Routes = [
{ path: '', component: ListaGastosComponent },
{
  path: 'admin',
  canActivate: [authGuard],   // el portero corre antes de activar /admin
  loadComponent: () => import('./admin.component').then((m) => m.AdminComponent),
},
];

Devolver un UrlTree (con createUrlTree) es la forma correcta de redirigir desde un guard: cancela la navegación actual y lanza otra hacia el login. El queryParams: { volverA: state.url } recuerda el destino para volver tras iniciar sesión —el mismo patrón de "ruta protegida" que hiciste en React—.

Antes (guard de clase) vs Ahora (guard funcional)

Los tipos de guard

Angular tiene varios porteros, cada uno para un momento distinto:

  • CanActivateFn — ¿puede ACTIVARSE esta ruta? (el más común: auth, permisos).
  • CanActivateChildFn — ¿pueden activarse las rutas HIJAS de esta?
  • CanDeactivateFn — ¿puede ABANDONARSE esta ruta? (avisar "tienes cambios sin guardar").
  • CanMatchFn — ¿esta ruta siquiera COINCIDE? (útil para servir rutas distintas según permisos, y evita descargar el chunk lazy si no coincide).

Todos son funciones que devuelven boolean / UrlTree (o una Promise/Observable de eso, para comprobaciones asíncronas).

La lógica del guard, en vivo

La decisión de un guard es lógica pura: dado un estado de sesión y un rol, ¿pasa o redirige? Pruébala aquí (simulando inject con objetos directos):

En Angular real, auth y urlDestino vendrían de inject(AuthService) y del state.url, y la redirección sería un router.createUrlTree(...). Pero la DECISIÓN —el corazón del guard— es exactamente esta lógica, que puedes testear sola.

¿Qué debe devolver un guard funcional para REDIRIGIR al usuario en lugar de solo bloquearlo?

Mini-reto

En el playground: 1) añade un pagoGuard que reciba un Auth con un campo plan: "free" | "pro" y redirija a /mejora-tu-plan si el usuario no es "pro"; 2) prueba los tres escenarios (invitado, free, pro); 3) escribe cómo se vería como CanActivateFn real, con inject(AuthService) y router.createUrlTree(...).

Qué sigue

Ya proteges rutas. Falta una pieza para páginas robustas: cargar los datos ANTES de mostrar la página, para que nunca aparezca vacía "parpadeando". Eso es un resolver, el tema de la próxima lección, que cierra la teoría del módulo antes del reto de routing.