Atualizado: 03/01/2025

Guardas de Rotas - Angular

Guardas de rotas (guards) permitem restringir a navegação por determinadas rotas. Por exemplo, se o acesso a um recurso específico exigir autenticação ou outras condições, podemos conceder ou negar o acesso ao usuário com base nessas verificações. Ou seja, os guards protegem o acesso aos recursos.

canActivate

canActivate é um dos tipos de guards que permite controlar o acesso a um recurso durante a navegação por rotas. O método canActivate() precisa implementar uma função com a seguinte definição:

type CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) =>
Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree;

Ela recebe dois parâmetros: os objetos ActivatedRouteSnapshot e RouterStateSnapshot, que contêm informações sobre a solicitação. O ActivatedRouteSnapshot permite acessar várias informações da solicitação, incluindo parâmetros de rota e query strings. Por exemplo, se a rota tiver o parâmetro id, podemos obtê-lo assim:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) : boolean {
    // obtendo o id
    console.log(route.params["id"]);
    
    // ...restante do código
}

O resultado do método normalmente retorna um valor booleano: true ou false. Para permitir o acesso ao recurso solicitado, a função deve retornar true. Caso contrário, para cancelar ou negar o acesso, ela deve retornar false.

Como exemplo, vamos definir o seguinte projeto da aplicação:

helloapp/
├── src/
│   ├── app/
│   │   ├── about.component.ts
│   │   ├── about.guard.ts
│   │   ├── app.component.ts
│   │   ├── app.config.ts
│   │   ├── home.component.ts
│   ├── main.ts  
│   ├── index.html 
├── angular.json    
├── package-lock.json
├── package.json
└── tsconfig.json

Vamos analisar os elementos do projeto. Na pasta src/app, temos dois componentes como exemplo. No arquivo home.component.ts, temos o seguinte componente HomeComponent:

import { Component } from "@angular/core";
  
@Component({
    selector: "home-app",
    template: `<h2>Principal</h2>`
})
export class HomeComponent { }

No arquivo about.component.ts, definimos o seguinte componente AboutComponent:

import { Component } from "@angular/core";
  
@Component({
    selector: "about-app",
    template: `<h2>Sobre o site</h2>`
})
export class AboutComponent { }

Suponha que desejamos restringir o acesso ao componente AboutComponent. Para isso, vamos adicionar um arquivo na pasta src/app, chamado about.guard.ts, com o seguinte código:

import { ActivatedRouteSnapshot, RouterStateSnapshot } from "@angular/router";
 
export const aboutGuard = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
    console.log(route.routeConfig?.path); // pode obter várias informações sobre as rotas, parâmetros, etc.
    return confirm("Você tem certeza de que deseja continuar?");
};

Aqui, definimos a função aboutGuard, que segue a definição de canActivate. Para testar ambos os cenários, chamamos o método confirm(), que exibe uma janela de diálogo para confirmação. Se o usuário cancelar, o método confirm() retornará false. Caso o usuário confirme, o retorno será true.

Além disso, o caminho solicitado é exibido através da propriedade route.routeConfig.path. Portanto, ao tomar a decisão de permitir ou não o redirecionamento do usuário, podemos explorar diversas informações.

Agora, vamos definir as referências para os componentes no arquivo app.component.ts:

import { Component } from "@angular/core";
import { RouterOutlet, RouterLink } from "@angular/router";
    
@Component({
    selector: "my-app",
    standalone: true,
    imports: [RouterOutlet, RouterLink],
    styles: `a {padding: 3px;}`,
    template: `<div>
                    <h1>Roteamento no Angular</h1>
                    <nav>
                        <a routerLink="">Principal</a>
                        <a routerLink="/about">Sobre o site</a>
                    </nav>
                    <router-outlet></router-outlet>
                </div>`
})
export class AppComponent {}

No arquivo app.config.ts, definimos e configuramos as rotas para os componentes:

import { ApplicationConfig } from "@angular/core";
import { provideRouter, Routes } from "@angular/router";
    
import { HomeComponent } from "./home.component";
import { AboutComponent } from "./about.component";
import { aboutGuard } from "./about.guard";
    
// definição das rotas
const appRoutes: Routes = [
    { path: "", component: HomeComponent },
    { path: "about", component: AboutComponent, canActivate: [aboutGuard] }
];
    
export const appConfig: ApplicationConfig = {
    providers: [provideRouter(appRoutes)]
};

Para restringir o acesso à rota /about, adicionamos o parâmetro canActivate: [aboutGuard] na definição da rota. Como resultado, ao tentar acessar /about, será exibida uma janela de confirmação.

Guardas de Rotas em Angular

Injeção de Serviços

Freqüentemente, a decisão de permitir o acesso a um recurso depende de dados armazenados. Por exemplo, podemos definir um novo arquivo auth.service.ts na pasta src/app:

import { Injectable } from "@angular/core";
 
@Injectable({
    providedIn: "root", // serviço global
})
export class AuthService {
    isLoggedIn = true;
    login(): void { this.isLoggedIn = true; }
    logout(): void { this.isLoggedIn = false; }
}

Aqui, definimos a classe AuthService como um serviço global. A variável isLoggedIn indica se o usuário está autenticado. Para fins de demonstração, consideramos que o usuário está logado.

Agora, vamos modificar o arquivo about.guard.ts para decidir a navegação com base na autenticação do usuário:

import { inject } from "@angular/core";
import { AuthService } from "./auth.service";
    
export const aboutGuard = () => {
    const authService = inject(AuthService); // obtemos o serviço
    return authService.isLoggedIn;
};

Essa é a mesma função aboutGuard, apenas sem os parâmetros, já que não são necessários neste caso. Dentro da função, usamos inject() para obter o serviço global AuthService e retornamos o valor da variável isLoggedIn.

canDeactivate

canDeactivate também permite controlar a navegação, mas de uma maneira diferente. Ele é útil em cenários onde o usuário está inserindo dados, mas ainda não os salvou, e decide sair da página. Nesse caso, podemos exibir um aviso ou uma janela de confirmação para evitar a perda de informações inseridas.

Comparando com canActivate, o canActivate verifica se o usuário pode acessar um componente específico, enquanto o canDeactivate verifica se ele pode sair de um determinado componente. A função canDeactivate tem a seguinte definição:

type CanDeactivateFn<T> = (component: T, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, 
nextState: RouterStateSnapshot) => Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree;

Vamos supor que queremos controlar a navegação a partir do componente AboutComponent. Alteramos o código dele da seguinte maneira:

import { Component } from "@angular/core";
import { RouterLink } from "@angular/router";
    
@Component({
    selector: "about-app",
    standalone: true,
    imports: [RouterLink],
    template: `<h2>Sobre o site</h2>
              <button (click)="save()">Salvar</button>`
})
export class AboutComponent { 
    saved: boolean = false;

    save() {
        this.saved = true;
    }
}

Para simular a funcionalidade, adicionamos a propriedade saved à classe AboutComponent, que indica se os dados foram salvos. Usando o método save(), acionado pelo clique de um botão, podemos controlar o valor dessa variável. Por exemplo, se o botão foi pressionado, significa que os dados foram salvos, e o valor de saved será true.

Para controlar a navegação a partir do AboutComponent, adicionamos o arquivo exit.about.guard.ts na pasta src/app com o seguinte código:

import { AboutComponent } from "./about.component";
  
export const exitAboutGuard = (component: AboutComponent) => {
    if (!component.saved) {
        return confirm("Você deseja sair da página?");
    }
    return true;
};

Na função exitAboutGuard, o parâmetro component recebe o tipo do componente de onde ocorre a navegação: neste caso, o AboutComponent. Dessa forma, podemos considerar o estado do componente ao realizar a navegação, especificamente a variável saved. Se this.saved for false (indicando que os dados não foram salvos), exibimos uma janela de confirmação.

Se a função retornar true, a navegação será permitida. Se retornar false, o usuário permanecerá na página.

Para ativar o exitAboutGuard, alteramos a rota para o AboutComponent no arquivo app.config.ts:

import { ApplicationConfig } from "@angular/core";
import { provideRouter, Routes } from "@angular/router";
    
import { HomeComponent } from "./home.component";
import { AboutComponent } from "./about.component";
import { exitAboutGuard } from "./exit.about.guard";
    
// definição das rotas
const appRoutes: Routes = [
    { path: "", component: HomeComponent },
    { path: "about", component: AboutComponent, canDeactivate: [exitAboutGuard] }
];
    
export const appConfig: ApplicationConfig = {
    providers: [provideRouter(appRoutes)]
};

Aqui, no campo da rota, incluímos o parâmetro:

canDeactivate: [exitAboutGuard]

Agora, ao tentar sair do componente AboutComponent (se saved for igual a false), será exibida uma janela de confirmação perguntando ao usuário se ele realmente deseja sair da página.

canDeactivate em Angular
Política de Privacidade

Copyright © www.programicio.com Todos os direitos reservados

É proibida a reprodução do conteúdo desta página sem autorização prévia do autor.

Contato: programicio@gmail.com