Saltar al contenido

Angular 2020: ¿Cómo usar Guard, CanActivate? Ejemplo de uso

diciembre 7, 2020

Siempre que construimos una aplicación web, existen rutas (direcciones o páginas) que solo pueden ser accedidas por los administradores de la aplicación. Un usuario no puede entrar a el listado de usuarios para borrarlos por ejemplo. Para eso sirven los Guards en Angular. Aprenderemos como usarlos.

Los Guards en Agular trabajan de la mano con una función llamada CanActive (hay otras, pero esta es la más sencilla de aplicar, ya que las otras cumplen la misma función pero a otros niveles de profundidad). Esta función debe usarse en la ruta que queremos controlar.

La función en la ruta es la que hará el llamado del Guard, y dependiendo lo que este devuelva, la ruta podrá activarse o mostrarse, o no. Por eso se llama así, CanActivate. Pero bueno, empecemos por el principio. Vamos crear nuestro primer Guard.

Para comenzar debes tener un proyecto, yo te recomendaría que inicies un proyecto nuevo, solo para hacer las pruebas, luego lo puedes aplicar a los proyecto que tengas en producción o en los que estés trabajando. Pero hacerlo con un proyecto nuevo te da libertad mental para hacer lo que se te ocurra si temor a dañar algo. Especialmente si estás aprendiendo.

Para crear un proyecto navegas desde la consola a la carpeta donde quieres crear el proyecto y ejecutas el comando de CLI. Si quieres saber más sobre esto, puedes ver el artículo donde lo explico: ¿Cómo instalar Angular y crear tu primer proyecto?

Preparemos el ejemplo de Guard

Para el ejemplo vamos a crear tres componentes, cada uno en un ruta diferente. Básicamente crearemos una pequeña navegación básica. Puedes crearlo de la forma que mejor te parezca, crear otro componente «Menú» o agregar un botón a cada componente que lleve a la otra ruta.

El objetivo es que podamos navegar. Yo usaré un link en cada componente. Pero antes creemos los componentes ¿Cierto? vamos a nuestra consola y ejecutamos los comandos para hacerlo. ng g c components/pagina01, ng g c components/pagina02, ng g c components/home.

Esto nos debe crear un directorio llamado components y dentro crearnos 3 directorios, uno para cada página que acabamos de crear.

Ahora vamos al archivo app-routing.module.ts y definamos algunas rutas que nos lleven a cada uno de los componentes. Antes las crearemos sin el CanActive, para probar el funcionamiento normal (no dudo de que Angular funcione bien, es solo una manía que he adquirido con los años).

El archivo debe quedar así:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './components/home/home.component';
import { Pagina01Component } from './components/pagina01/pagina01.component';
import { Pagina02Component } from './components/pagina02/pagina02.component';

const routes: Routes = [
  {path:'pagina01', component: Pagina01Component},
  {path:'pagina02', component: Pagina02Component},
  {path:'', component: HomeComponent},
];

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

Tenemos 3 rutas, una que lleva a cada componente, para realizar las pruebas puedes escribir los paths en la barra de direcciones de tu navegador: http://localhost:4200/pagina02.

Esto debería mostrarte el típico mensaje Pagina02 works. Si no lo vez puede que este hasta abajo de todo de la página que crear por defecto Angular, si quieres velo mejor ve al archivo app.components.html y borra todo el contenido. Solo deja el <router-outlet></router-outlet>.

Deberías ver una pantalla completamente blanca en tu navegador solo con las palabras Pagina02 works. Es decir, lo que haya en el componente pagina02 que creamos antes. Si pones el resto de direcciones podrías ver los componentes también. Excelente. Ya tenemos nuestras rutas. Ahora creemos nuestro Guard.

Creando nuestro primer Guard

Para crear nuestro Guard podemos hacerlo de manera manual. Sin embargo Angular CLI nos ofrece una manera de crearlos automáticamente. Para hacerlo usamos el siguiente comando: ng g guard guards/horaAcceso

Si ya has trabajado con comandos ng verás que es muy intuitivo. g es de generar, guard porque queremos generar un Guard, guard/ es el directorio donde queremos guardar nuestro archivo y horaAcceso es el nombre del Guard. ¿Sencillo cierto?

Cuando ejecutas el comando te aparece algo así como un menú donde tienes que seleccionar el tipo de Guard que quieres crear. Por defecto viene seleccionada CanActivate, pero si quieres cambiarlo puede usar las flechas de navegación y seleccionar el tipo de Guard usando la barra espaciadora del teclado.

Para crearlo solo debes dar Enter y listo, Angular CLI se encargará de crear el directorio y el archivo. También hará todas las importaciones necesarias para que el Guard funcione. Nosotros solo tendremos que modificarlo para que funcione como nosotros queremos.

El archivo generado tiene que estar en la carpeta guards/ tenemos que acceder al archivo con el nombre hora-acceso.guard.ts, que es nuestro Guard, el otro archivo que termina con .spec.ts no lo tocaremos por el momento. Actualmente quedémonos en el .guard.ts

Actualmente el archivo debe tener el siguiente contenido:

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class HoraAccesoGuard implements CanActivate {
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return true;
  }
  
}

Nosotros tenemos que editarlo para que cumpla nuestra necesidad. El ejemplo que se me ocurre es verificar la hora y que no permita el acceso a la página si ya es más de las 10:00 pm. Claro que se pueden hacer todo tipo de validaciones, verificar que existe un token, verificar el rol del usuario activo. Verficando un Service el límite es tu imaginación.

Pero, para este ejemplo que tiene por objetivo demostrarte como funcionan los Guard, creo que meterle más complejidad sería innecesario. Solo tienes que saber como funciona, luego puedes hacer todo tipo de validaciones. El archivo debe quedar así:

import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';

@Injectable({
  providedIn: 'root'
})
export class HoraAccesoGuard implements CanActivate {

  // Ya que vamos a hacer un redirección si la hora es mayor de 22
  // Necesitamos importar el Router e inyectarlo al construictor
  constructor(private router:Router){}


  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot):  boolean {

    // Obtenemos la hora actual  
    const hora = new Date().getHours();
    
    // Comparamos la hora con el maximo permitido
    // Esto sería en caso de que no queremos que 
    // pueda entrar a la página después de las 10:00 pm  
    if (hora >= 22) {
      // Si la hora es mayor o igual redireccionamos al homeComponent
      this.router.navigate(['']);
      // Si devolvemos FALSE no de permitirá el acceso
      return false;
    }

    // Si devolvemos TRUE si se permitirá el acceso.
    return true;
  }
  
}

El código está comentado para facilitar su entendimiento, hacemos, basta con leerlo para saber qué hace cada línea. Lo más importante sucede en la línea número 24 y en la línea 28. En la línea 24 estamos verificando si la condición para NO cargar la página se cumple. Y en la línea 28 devolvemos un false en el caso de que suceda.

El router es necesario para hacer una redirección, de lo contrarío veríamos una página blanca, sin con componente pero vacia, no tendría mucho sentido para el usuario. Así que usamos el router para redirigir al usuario al Home. Otra opción sería crear un componente de AccesoDenegado y redirigir ahí cuando la condición se cumpla.

Si la condición no se cumple entonces devolvemos un true, eso le dice a Angular que puede cargar la ruta sin problemas. Bueno. Ya tenemos nuestro Guard configurado, ahora tenemos que aplicarlo a la ruta que necesitamos verificar. Así que agregamos lo siguiente al archivo de rutas. Específicamente a la ruta que queremos afectar:

{path:'pagina02', component: Pagina02Component, canActivate: [HoraAccesoGuard]},

Lo nuevo es el valor canActivate: [ HoraAccesoGuard ] este argumento aplica los Guard. Como puedes ver canActivate recibe un array de Guard, lo que significa que perfectamente puedes enviar más de uno, seperándolos por comas ( , ) como cualquier Array.

Ya con eso estamos aplicando el Guard a la ruta, puedes comprobarlo entrando a la ruta http://localhost:4200/pagina02/ claro que para que la condición para mostrar la ruta es que sea antes de las 10:00 pm. Así que cuando lo pruebes puedes cambiar el valor del if (hora >= 22 ) { en el Guard. Si pon una hora que ya haya pasado, por ejemplo, si lo estás haciendo a las 9:00 am, puedes poner if (hora >= 8) { con eso, Angular no te dejaría entrar a la página y te redireccionaría al Home.

Bueno, eso fue todo por el artículo de hoy, espero que les haya gustado y sobre todo espero que les haya servido. Si tienes alguna duda del funcionamiento de los Guard o cualquier otra duda con Angular, puedes dejarla en los comentario y con gusto intentaré ayudarte. Un abrazo y hasta el siguiente código.