Angular has five types of route guards for controlling access to specific routes. Learn how to implement them in five steps, with the CanActivate route guard as an example.
A route guard is a feature that controls access to specific routes in the Angular application. It can protect routes from unauthorized access, require certain conditions be met before navigating to a route or perform other checks and actions during the navigation process.
Angular provides five types of route guards:
Most of the time, you use either CanActivate or CanActivateChild. The CanActivate guard determines whether a route can be activated, and the CanActivateChild determines whether a child route can be activated.
In this post, we will learn to create and apply a CanActivate route guard to the routes.
You can create a route with lazy-loaded components as below:
export const routes: Routes = [
{
path: 'login',
loadComponent: () =>
import('./login/login.component').then((m) => m.LoginComponent),
},
{
path: '',
children: [
{
path: 'home',
loadComponent: () =>
import('./home/home.component').then((m) => m.HomeComponent),
},
{
path: 'product',
loadComponent: () =>
import('./product/product.component').then((m) => m.ProductComponent),
},
{
path: 'invoice',
loadComponent: () =>
import('./invoice/invoice.component').then((m) => m.InvoiceComponent),
},
{
path: '',
redirectTo: '/home',
pathMatch: 'full',
},
{
path: '**',
component: PageNotFoundComponent,
},
],
},
];
As shown in the routes above, several components, such as ProductComponent, InvoiceComponent and HomeComponent, are lazily loaded. Additionally, a LoginComponent will handle navigation for unauthorized users, redirecting them to the login route.
One crucial point about the Login route is that it is not included as a child of the main route.
In the newer versions of Angular, you must provide the route at the ApplicationConfig, as demonstrated below.
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideHttpClient(),
provideAnimationsAsync()]
};
In the Auth Service, we will create two separate functions: one to handle the Login operation and another to read the token from local storage to determine whether the user is logged in.
If you’re familiar with the basics of Angular, you already know how to perform a POST operation to a REST endpoint. In this case, we set the Content-Type header and use HttpClient to make the POST request to the Login endpoint.
login(user: ILoginUser): Observable<any> {
const headers = new HttpHeaders({
'Content-Type': 'application/json',
});
return this.httpClient.post('http://localhost:3000/login', user, {
headers,
});
}
The isLoggedIn function is created as shown below:
isLoggedIn(): Observable<boolean> {
const token = localStorage.getItem('token');
if (token) {
return of(true);
}
return of(false);
}
The isLoggedIn
function is designed to determine whether a user is currently logged in by checking for the presence of a token in the browser’s local storage and whether the retrieved token exists.
The user is logged in if the token is found (i.e., not null or undefined). If a token exists, it returns an observable that emits true; otherwise, it returns an observable that emits false.
Bringing it all together, the AuthService looks like the following code snippet:
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { inject, Injectable } from '@angular/core';
import { ILoginUser } from './loginuser.interface';
import { Observable, of } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class AuthService {
private httpClient = inject(HttpClient);
public redirectUrl: string | null = null;
constructor() {}
login(user: ILoginUser): Observable<any> {
const headers = new HttpHeaders({
'Content-Type': 'application/json',
});
return this.httpClient.post('http://localhost:3000/login', user, {
headers,
});
}
isLoggedIn(): Observable<boolean> {
const token = localStorage.getItem('token');
if (token) {
return of(true);
}
return of(false);
}
}
Take note of the redirectUrl declaration. You will use it to navigate back to the route where the login process was originally initiated.
In the Login component, set up a reactive form that includes fields for the username and password.
this.loginForm = this.fb.group({
username: ['', [Validators.required]],
password: ['', [Validators.required]],
});
Then, the template binds the reactive form to the HTML controls, as shown below.
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="username">Username</label>
<input type="text" class="form-control" formControlName="username" id="username">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" formControlName="password" id="password">
</div>
<button type="submit" class="btn btn-primary">Login</button>
</form>
Create the onSubmit
method to handle the click event for the Login button.
onSubmit(): void {
if (this.loginForm.valid) {
this.authService.login(this.loginForm.value).subscribe((data) => {
if (data) {
localStorage.setItem('token', data.token);
const redirectUrl = this.authService.redirectUrl
? this.authService.redirectUrl
: '/';
this.router.navigate([redirectUrl]);
this.authService.redirectUrl = null;
}
});
}
}
The onSubmit
method performs the following tasks:
authService
.In summary, it performs the login operation, saves the token and redirects to the URL set in the service.
Finally, apply the route guard to the route. In this case, use the canActivate
route guard on the main route, as demonstrated below.
export const routes: Routes = [
{
path: 'login',
loadComponent: () =>
import('./login/login.component').then((m) => m.LoginComponent),
},
{
path: '',
canActivate: [authGuard],
children: [
// all paths
],
},
];
These steps are required to add a route guard in an Angular application. To summarize, a route guard in Angular helps control who can access specific pages in your app. It helps protect pages from users who shouldn’t see them, like those who aren’t logged in, and it requires certain conditions be met before opening a page.
I hope you find this post valuable and confident in implementing route guards in your Angular application.
Dhananjay Kumar is an independent trainer and consultant from India. He is a published author, a well-known speaker, a Google Developer Expert, and a 10-time winner of the Microsoft MVP Award. He is the founder of geek97, which trains developers on various technologies so that they can be job-ready, and organizes India's largest Angular Conference, ng-India. He is the author of the best-selling book on Angular, Angular Essential. Find him on Twitter or GitHub.