Guardas de Rotas em 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.

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.
