Route Guards funcionales en Angular 21
Un route guard en Angular permite controlar la navegación a una ruta específica y decidir si dicha navegación debe ser permitida o no.
En Angular 21, los guards se implementan como funciones que utilizan inject() para acceder a servicios. Este enfoque funcional es el estándar recomendado por su simplicidad, mejor tree-shaking y compatibilidad natural con el patrón standalone.
Tipos de guards funcionales
CanActivateFn: Se ejecuta antes de que se active una ruta. Se usa principalmente para proteger rutas que requieren autenticación.CanDeactivateFn: Se utiliza para manejar la navegación fuera de una ruta. Ideal para advertir sobre cambios no guardados.CanActivateChildFn: Similar aCanActivateFn, pero se aplica a todas las rutas hijas.CanMatchFn: Determina si una configuración de ruta coincide con la URL actual. Útil para lazy loading condicional.ResolveFn: Permite pre-cargar datos antes de que se active una ruta.
Crear un AuthGuard funcional (CanActivateFn)
Generar un guard
Utilizamos Angular CLI:
ng generate guard auth
Implementar el guard funcional
// 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 authService = inject(AuthService);
const router = inject(Router);
if (authService.isLoggedIn()) {
return true;
}
return router.parseUrl('/login');
};
Implementar el servicio de autenticación
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class AuthService {
private authenticated: boolean = false;
login(username: string, password: string): boolean {
if (username === 'user' && password === 'password') {
this.authenticated = true;
return true;
}
return false;
}
isLoggedIn(): boolean {
return this.authenticated;
}
logout() {
this.authenticated = false;
}
}
Configurar rutas con el guard
Las rutas se configuran en app.routes.ts con provideRouter() en app.config.ts:
// app.routes.ts
import { Routes } from '@angular/router';
import { authGuard } from './auth.guard';
export const routes: Routes = [
{
path: 'home',
loadComponent: () => import('./home/home.component').then(m => m.HomeComponent),
canActivate: [authGuard]
},
{
path: 'login',
loadComponent: () => import('./login/login.component').then(m => m.LoginComponent)
},
{ path: '', redirectTo: '/home', pathMatch: 'full' }
];
Ejemplos prácticos
Confirmación de salida con CanDeactivateFn
Este guard previene que un usuario abandone una página accidentalmente sin guardar cambios:
// can-deactivate.guard.ts
import { CanDeactivateFn } from '@angular/router';
export type CanDeactivateComponent = {
canDeactivate: () => boolean | Promise<boolean> | import('rxjs').Observable<boolean>
};
export const canDeactivateGuard: CanDeactivateFn<CanDeactivateComponent> =
(component) => {
return component.canDeactivate ? component.canDeactivate() : true;
};
Implementación en el componente:
// edit.component.ts
import { Component } from '@angular/core';
import { CanDeactivateComponent } from './can-deactivate.guard';
@Component({
selector: 'app-edit',
template: '<h1>Edit Component</h1>',
})
export class EditComponent implements CanDeactivateComponent {
canDeactivate(): boolean {
return confirm('Do you want to discard changes?');
}
}
Para usar este guard en las rutas:
// app.routes.ts
import { Routes } from '@angular/router';
import { EditComponent } from './edit.component';
import { canDeactivateGuard } from './can-deactivate.guard';
export const routes: Routes = [
{
path: 'edit',
component: EditComponent,
canDeactivate: [canDeactivateGuard]
},
];
Cargar datos con ResolveFn
El resolver funcional permite precargar datos antes de activar una ruta:
// data.resolver.ts
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { DataService } from './data.service';
export const dataResolver: ResolveFn<any> = (route, state) => {
const dataService = inject(DataService);
return dataService.getData();
};
En la configuración de rutas:
export const routes: Routes = [
{
path: 'home',
component: HomeComponent,
resolve: { data: dataResolver }
},
];
Proteger rutas hijas con CanActivateChildFn
// child-auth.guard.ts
import { inject } from '@angular/core';
import { CanActivateChildFn, Router } from '@angular/router';
import { AuthService } from './auth.service';
export const childAuthGuard: CanActivateChildFn = (childRoute, state) => {
const authService = inject(AuthService);
const router = inject(Router);
if (authService.isLoggedIn()) {
return true;
}
return router.parseUrl('/login');
};
En la configuración de rutas, el guard se aplica a la ruta padre y protege todas sus rutas hijas:
const routes: Routes = [
{
path: 'dashboard',
component: DashboardComponent,
canActivateChild: [childAuthGuard],
children: [
{ path: 'profile', component: ProfileComponent },
{ path: 'settings', component: SettingsComponent },
],
},
];
Controlar lazy loading con CanMatchFn
CanMatchFn reemplaza al antiguo CanLoad y determina si una ruta coincide con la URL actual, permitiendo lazy loading condicional:
// match.guard.ts
import { inject } from '@angular/core';
import { CanMatchFn, Router } from '@angular/router';
import { AuthService } from './auth.service';
export const matchGuard: CanMatchFn = (route, segments) => {
const authService = inject(AuthService);
if (authService.isLoggedIn()) {
return true;
}
return inject(Router).parseUrl('/login');
};
En la configuración de rutas:
export const routes: Routes = [
{
path: 'admin',
loadChildren: () => import('./admin/admin.routes').then(m => m.default),
canMatch: [matchGuard],
},
];
Enfoque legacy: Guards basados en clases
En versiones anteriores de Angular, los guards se implementaban como clases que implementaban interfaces como CanActivate, CanDeactivate, etc. Este enfoque legacy sigue funcionando en aplicaciones existentes pero no es el recomendado para nuevos proyectos en Angular 21.
// Enfoque legacy (no recomendado en Angular 21)
@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
if (this.authService.isLoggedIn()) {
return true;
}
this.router.navigate(['login']);
return false;
}
}
El enfoque funcional con CanActivateFn, CanDeactivateFn, CanMatchFn y ResolveFn es más conciso, tiene mejor tree-shaking y se integra naturalmente con el patrón standalone de Angular 21.
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
Entender los diferentes tipos de Route Guards funcionales en Angular. Implementar CanActivateFn para proteger rutas que requieren autenticación. Usar CanDeactivateFn para manejar navegación fuera de una ruta. Proteger rutas hijas con CanActivateChildFn. Pre-cargar datos antes de la activación de una ruta con ResolveFn. Integrar y configurar Route Guards funcionales en aplicaciones standalone.