Descubre cómo usar los guards en Angular para controlar la navegación y proteger rutas en tus aplicaciones. Aprende con ejemplos prácticos y mejora la seguridad de tu aplicación Angular.
Los guards en Angular son una herramienta esencial para controlar la navegación y proteger rutas en tus aplicaciones. En este artículo, te proporcionaremos una guía completa sobre los diferentes tipos de guards disponibles en Angular, cómo implementarlos y cuándo usarlos para garantizar la seguridad y una experiencia de usuario fluida.
En Angular, los guards son servicios que permiten controlar el acceso a las rutas en tu aplicación. Son una forma de definir la lógica que determina si una ruta puede ser activada, desactivada o cargada. Esto te ayuda a proteger rutas y manejar la navegación de los usuarios en tu aplicación de manera más eficiente.
El guard CanActivate
se utiliza para decidir si una ruta puede ser activada. Esto es útil para verificar, por ejemplo, si un usuario está autenticado antes de permitirle acceder a una ruta específica.
Ejemplo Real:
Imagina que tienes una aplicación con una ruta para el perfil de usuario que solo debe ser accesible si el usuario está autenticado. Aquí está cómo implementar CanActivate:
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthService } from './auth.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(): boolean {
if (this.authService.isAuthenticated()) {
return true;
} else {
this.router.navigate(['/login']);
return false;
}
}
}
Uso en el enrutador (router):
const routes: Routes = [
{
path: 'profile',
component: ProfileComponent,
canActivate: [AuthGuard]
}
];
El guard CanDeactivate se usa para prevenir la navegación fuera de una ruta si hay cambios no guardados. Es ideal para formularios o componentes donde el usuario podría perder datos si navega a otra página.
Ejemplo Real:
Supón que tienes un componente de formulario y quieres asegurarte de que el usuario confirme antes de navegar fuera si hay cambios no guardados:
import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { Observable } from 'rxjs';
export interface CanComponentDeactivate {
canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}
@Injectable({
providedIn: 'root'
})
export class UnsavedChangesGuard implements CanDeactivate<CanComponentDeactivate> {
canDeactivate(component: CanComponentDeactivate): Observable<boolean> | Promise<boolean> | boolean {
return component.canDeactivate ? component.canDeactivate() : true;
}
}
Uso en el componente:
import { Component } from '@angular/core';
import { CanComponentDeactivate } from './unsaved-changes.guard';
@Component({
selector: 'app-edit-profile',
templateUrl: './edit-profile.component.html'
})
export class EditProfileComponent implements CanComponentDeactivate {
hasUnsavedChanges = true;
canDeactivate(): boolean {
if (this.hasUnsavedChanges) {
return confirm('You have unsaved changes. Do you really want to leave?');
}
return true;
}
}
Uso en el enrutador:
const routes: Routes = [
{
path: 'edit-profile',
component: EditProfileComponent,
canDeactivate: [UnsavedChangesGuard]
}
];
El guard CanLoad se utiliza para prevenir la carga de módulos perezosos (lazy-loaded modules) si no se cumplen ciertas condiciones. Es útil para asegurar que los módulos sólo se carguen cuando el usuario tenga permiso.
Ejemplo Real:
Supongamos que tienes un módulo de administración que solo debe cargarse si el usuario es un administrador:
import { Injectable } from '@angular/core';
import { CanLoad, Route, UrlSegment, Router } from '@angular/router';
import { AuthService } from './auth.service';
@Injectable({
providedIn: 'root'
})
export class AdminLoadGuard implements CanLoad {
constructor(private authService: AuthService, private router: Router) {}
canLoad(route: Route, segments: UrlSegment[]): boolean {
if (this.authService.isAdmin()) {
return true;
} else {
this.router.navigate(['/no-access']);
return false;
}
}
}
Uso en el enrutador:
const routes: Routes = [
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
canLoad: [AdminLoadGuard]
}
];
El guard Resolve se usa para pre-cargar datos antes de activar una ruta. Esto asegura que los datos necesarios estén disponibles antes de que el componente asociado con la ruta se cargue.
Ejemplo Real:
Imagina que tienes una página de detalles de producto y quieres cargar la información del producto antes de que se muestre la página:
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable, of } from 'rxjs';
import { ProductService } from './product.service';
import { catchError } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class ProductResolver implements Resolve<any> {
constructor(private productService: ProductService) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
const productId = route.paramMap.get('id');
return this.productService.getProduct(productId).pipe(
catchError(error => {
console.error('Error loading product', error);
return of(null);
})
);
}
}
Uso en el componente:
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-product-detail',
templateUrl: './product-detail.component.html'
})
export class ProductDetailComponent implements OnInit {
product: any;
constructor(private route: ActivatedRoute) {}
ngOnInit(): void {
this.route.data.subscribe(data => {
this.product = data.product;
});
}
}
Uso en el enrutador:
const routes: Routes = [
{
path: 'product/:id',
component: ProductDetailComponent,
resolve: { product: ProductResolver }
}
];
Para implementar un guard, debes seguir estos pasos:
ng generate guard auth
Implementar la Lógica: Define la lógica específica en el guard para controlar la navegación.
Registrar el Guard: Añade el guard a las rutas en tu módulo de enrutamiento.
const routes: Routes = [
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [AuthGuard]
},
{
path: 'edit',
component: EditComponent,
canDeactivate: [UnsavedChangesGuard]
}
];
Ejemplo Completo de una Aplicación
Supongamos que estás construyendo una aplicación de comercio electrónico con las siguientes características:
Enrutador Configurado:
const routes: Routes = [
{
path: 'profile',
component: ProfileComponent,
canActivate: [AuthGuard]
},
{
path: 'edit-profile',
component: EditProfileComponent,
canDeactivate: [UnsavedChangesGuard]
},
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
canLoad: [AdminLoadGuard]
},
{
path: 'product/:id',
component: ProductDetailComponent,
resolve: { product: ProductResolver }
},
{
path: 'login',
component: LoginComponent
},
{
path: 'no-access',
component: NoAccessComponent
}
];
Los guards en Angular son fundamentales para controlar el acceso y la navegación en aplicaciones complejas. Usando CanActivate, CanDeactivate, CanLoad y Resolve, puedes manejar de manera efectiva la seguridad y la experiencia del usuario en tu aplicación. Los ejemplos proporcionados muestran cómo puedes aplicar estos guards en situaciones prácticas para mejorar la robustez de tu aplicación.