Route Guards basados en interfaces

Avanzado
Angular
Angular
Actualizado: 27/08/2024

¡Desbloquea el curso completo!

IA
Ejercicios
Certificado
Entrar

¿Qué es un route guard?

Un route guard en Angular es una interfaz que permite controlar la navegación a una ruta específica y decidir si dicha navegación debe ser permitida o no

Los route guards se utilizan comúnmente para implementar lógicas de autenticación y autorización, aunque también pueden servir para diversas comprobaciones o condiciones antes de que se cargue una ruta.

Ejemplos de route guards basados en interfaces

  1. CanActivate: Este guard se ejecuta antes de que se active una ruta. Se usa principalmente para proteger rutas que requieren autenticación.
  2. CanDeactivate: Se utiliza para manejar la navegación fuera de una ruta. Es útil, por ejemplo, para advertir a los usuarios sobre la posible pérdida de cambios no guardados.
  3. CanActivateChild: Similar al CanActivate, pero se aplica a rutas hijas. Permite proteger rutas secundarias dentro de una aplicación.
  4. CanLoad: Se usa para evitar que se cargue un módulo que se está cargando de manera diferida hasta que se cumpla alguna condición. Ideal para optimizar la carga de recursos en aplicaciones grandes.
  5. Resolve: Permite pre-cargar datos antes de que se active una ruta. Este guard es útil para asegurar que los datos necesarios estén disponibles antes de que la vista sea renderizada.

¿Te está gustando esta lección?

Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.

Progreso guardado
Asistente IA
Ejercicios
Iniciar sesión gratis

Más de 25.000 desarrolladores ya confían en CertiDevs

Crear un AuthGuard (CanActivate)

Para crear un guard utilizando la interfaz CanActivate, seguimos estos pasos:

Generar un nuevo guard

Utilizamos Angular CLI para generar un guard. Esto se hace de la siguiente manera:

1.- Abre una terminal en el directorio raíz de tu proyecto Angular.

2.- Ejecuta el siguiente comando:

ng generate guard auth

O la versión abreviada:

ng g guard auth

3.- Angular CLI te preguntará qué interfaces quieres implementar. Selecciona CanActivate usando las teclas de flecha y presiona la barra espaciadora para marcarla. Luego presiona Enter.

4.- El CLI generará el archivo auth.guard.ts en la carpeta src/app.

Implementar el método canActivate

En el archivo generado (auth.guard.ts), implementamos la lógica de verificación de autenticación.

Ejemplo básico de uso

Para implementar un AuthGuard en Angular, primero debemos crear un guard utilizando una de las interfaces de guard disponibles. En este ejemplo, usaremos CanActivate, pero Angular ofrece otras interfaces como CanDeactivate, CanActivateChild, y CanLoad para diferentes escenarios.

A continuación, se detalla el proceso paso a paso para crear un AuthGuard básico, implementar un servicio de autenticación y proteger las rutas en la aplicación. Este enfoque se puede adaptar para usar otras interfaces de guard según las necesidades específicas del proyecto.

Aplicaciones con módulo raíz tradicional

Implementación del AuthGuard

Primero, creamos el AuthGuard. Debemos asegurarnos de que este guard verifica si el usuario está autenticado antes de permitir el acceso a una ruta específica.

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { AuthService } from './auth.service'; // Servicio de autenticación
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    const isAuthenticated = this.authService.isLoggedIn(); // Método para verificar si está autenticado
    if (!isAuthenticated) {
      this.router.navigate(['login']); // Redirige al login si no está autenticado
      return false;
    }
    return true;
  }
}

Implementación del servicio de autenticación

El siguiente paso es implementar el servicio de autenticación (AuthService). Este servicio debe tener un método que verifique si el usuario está autenticado o no.

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  private authenticated: boolean = false;

  // Simulación de login
  login(username: string, password: string): boolean {
    if (username === 'user' && password === 'password') {
      this.authenticated = true;
      return true;
    }
    return false;
  }

  // Método que comprueba el estado de autenticación
  isLoggedIn(): boolean {
    return this.authenticated;
  }

  // Simulación de logout
  logout() {
    this.authenticated = false;
  }
}

Integración del AuthGuard en las rutas de la aplicación

Una vez que tenemos implementado tanto el AuthGuard como el AuthService, podemos proceder a la integración del guard en las rutas de la aplicación para protegerlas. 

Modificamos el módulo de rutas (AppRoutingModule) para incluir el guard en las rutas que queremos proteger.

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './auth.guard';
import { HomeComponent } from './home/home.component'; // Componente protegido
import { LoginComponent } from './login/login.component'; // Componente de login

const routes: Routes = [
  { path: 'home', component: HomeComponent, canActivate: [AuthGuard] },
  { path: 'login', component: LoginComponent },
  { path: '**', redirectTo: 'home' },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

Aplicaciones con Standalone Components

Implementación del AuthGuard

En aplicaciones standalone, el AuthGuard se implementa como una función en lugar de una clase. Esto permite una sintaxis más concisa y el uso de la función inject() para la inyección de dependencias.

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

export const authGuard = () => {
  const authService = inject(AuthService);
  const router = inject(Router);

  if (authService.isLoggedIn()) {
    return true;
  }
  return router.parseUrl('/login');
};

Implementación del servicio de autenticación

El servicio de autenticación permanece igual que en el ejemplo anterior, ya que los servicios funcionan de manera similar tanto en aplicaciones tradicionales como en standalone.

Configuración de rutas con el AuthGuard

En aplicaciones standalone, las rutas se configuran directamente en el archivo principal (generalmente main.ts). Aquí, utilizamos bootstrapApplication y provideRouter para configurar la aplicación y sus rutas.

// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { Routes, provideRouter } from '@angular/router';
import { AppComponent } from './app/app.component';
import { authGuard } from './app/auth.guard';

const routes: Routes = [
  {
    path: 'home',
    loadComponent: () => import('./app/home/home.component').then(m => m.HomeComponent),
    canActivate: [authGuard]
  },
  {
    path: 'login',
    loadComponent: () => import('./app/login/login.component').then(m => m.LoginComponent)
  },
  { path: '', redirectTo: '/home', pathMatch: 'full' },
];

bootstrapApplication(AppComponent, {
  providers: [provideRouter(routes)]
}).catch(err => console.error(err));

Ejemplos prácticos

Crear un AuthGuard (CanActivate)

Para crear un guard utilizando la interfaz CanActivate, seguimos estos pasos:

  1. Generar un nuevo guard: Utilizamos Angular CLI para generar un guard.
  2. Implementar el método canActivate: En el archivo generado (auth.guard.ts), implementamos la lógica de verificación de autenticación.

Confirmación de salida con CanDeactivate

Este guard se utiliza para prevenir que un usuario abandone una página accidentalmente sin guardar cambios. Es particularmente útil en formularios o editores.

El CanDeactivateGuard implementa la interfaz CanDeactivate. Comprueba si el componente tiene un método canDeactivate() y lo ejecuta. Este método puede devolver un booleano, una promesa o un observable.

// 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;
  };

En el componente, implementamos la lógica real de confirmación. En este caso, se muestra un simple cuadro de diálogo de confirmación, pero en una aplicación real, podría comprobar si hay cambios no guardados y mostrar un mensaje más específico.

// edit.component.ts
import { Component } from '@angular/core';
import { CanDeactivateComponent } from './can-deactivate.guard';

@Component({
  selector: 'app-edit',
  standalone: true,
  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] 
  },
  // ... otras rutas
];

Cargar datos con Resolve

El resolver es una herramienta poderosa para precargar datos antes de que se active una ruta. Esto asegura que la información necesaria esté disponible antes de que se muestre el componente, mejorando la experiencia del usuario.

En este ejemplo, el DataResolver implementa la interfaz Resolve. Utiliza un DataService para obtener los datos. El método resolve() se ejecuta antes de que la ruta se active, y la navegación no se completa hasta que los datos se resuelven.

// data.resolver.ts
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { Observable } from 'rxjs';
import { DataService } from './data.service';

export const dataResolver: ResolveFn<any> = (
  route,
  state
): Observable<any> => {
  const dataService = inject(DataService);
  return dataService.getData();
};

Suponiendo que tenemos un DataService:

// data.service.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  getData(): Observable<any> {
    return of({ message: 'Datos precargados' });
  }
}

En la configuración de rutas, asociamos el resolver a una ruta específica. Los datos resueltos estarán disponibles en el componente a través de ActivatedRoute.

// app.routes.ts
import { Routes } from '@angular/router';
import { HomeComponent } from './home.component';
import { dataResolver } from './data.resolver';

export const routes: Routes = [
  { 
    path: 'home', 
    component: HomeComponent, 
    resolve: { data: dataResolver } 
  },
  // ... otras rutas
];

Proteger rutas hijas con CanActivateChild

CanActivateChild es útil cuando tenemos un conjunto de rutas hijas que queremos proteger con la misma lógica de autorización. En lugar de aplicar un guard a cada ruta hija individualmente, podemos usar CanActivateChild en la ruta padre.

El ChildAuthGuard implementa CanActivateChild. Su lógica es similar a un CanActivate normal, pero se aplica a todas las rutas hijas. En este caso, verifica si el usuario está autenticado antes de permitir el acceso a cualquier ruta hija.

// 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);

  const isAuthenticated = authService.isLoggedIn();
  if (!isAuthenticated) {
    router.navigate(['login']);
    return false;
  }
  return true;
};

Suponiendo que tenemos un AuthService:

// auth.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  isLoggedIn(): boolean {
    // Implementación real de verificación de autenticación
    return true; // Ejemplo simplificado
  }
}

En la configuración de rutas, aplicamos el guard a la ruta padre (dashboard), y todas sus rutas hijas heredarán esta protección.

const routes: Routes = [
  {
    path: 'dashboard',
    component: DashboardComponent,
    canActivateChild: [ChildAuthGuard],
    children: [
      { path: 'profile', component: ProfileComponent },
      { path: 'settings', component: SettingsComponent },
    ],
  },
];

Controlar la carga diferida con CanLoad

CanLoad es un guard especialmente diseñado para trabajar con la carga diferida (lazy loading) de módulos. Previene que un módulo se cargue si no se cumplen ciertas condiciones, lo que puede ser útil para optimizar el rendimiento y la seguridad.

El LoadGuard implementa CanLoad. Verifica si el usuario está autenticado antes de permitir la carga del módulo. Si el usuario no está autenticado, redirige al login y previene la carga del módulo.

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

export const loadGuard: CanLoadFn = (route, segments) => {
  const authService = inject(AuthService);
  const router = inject(Router);

  const isAuthenticated = authService.isLoggedIn();
  if (!isAuthenticated) {
    router.navigate(['login']);
    return false;
  }
  return true;
};

En la configuración de rutas, aplicamos CanLoad a una ruta que usa loadChildren para cargar un módulo de forma diferida. Esto asegura que el módulo solo se cargará si el guard lo permite.

// app.routes.ts
import { Routes } from '@angular/router';
import { loadGuard } from './load.guard';

export const routes: Routes = [
  {
    path: 'admin',
    loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES),
    canLoad: [loadGuard],
  },
  // ... otras rutas
];

Para el módulo admin, ahora usaremos un archivo de rutas standalone:

// admin/admin.routes.ts
import { Routes } from '@angular/router';
import { AdminComponent } from './admin.component';

export const ADMIN_ROUTES: Routes = [
  { path: '', component: AdminComponent },
  // ... otras rutas admin
];

El componente Admin puede ser standalone:

// admin/admin.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-admin',
  standalone: true,
  template: '<h1>Admin Dashboard</h1>'
})
export class AdminComponent {}

Aprendizajes de esta lección

   - Entender los diferentes tipos de Route Guards en Angular.

   - Implementar CanActivate para proteger rutas que requieren autenticación.

   - Usar CanDeactivate para manejar navegación fuera de una ruta.

   - Proteger rutas hijas con CanActivateChild.

   - Controlar la carga diferida de módulos con CanLoad.

   - Pre-cargar datos antes de la activación de una ruta con Resolve.

   - Integrar y configurar Route Guards en aplicaciones tanto tradicionales como standalone.

Completa Angular y certifícate

Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.

Asistente IA

Resuelve dudas al instante

Ejercicios

Practica con proyectos reales

Certificados

Valida tus conocimientos

Más de 25.000 desarrolladores ya se han certificado con CertiDevs

⭐⭐⭐⭐⭐
4.9/5 valoración