Angular Router: Using Route Guards

The Angular router’s navigation guards allow to grant or remove access to certain parts of the navigation. Another route guard, the CanDeactivate guard, even allows you to prevent a user from accidentally leaving a component with unsaved changes.

Note that client-side route guards like this are not meant to be a security feature. They won't prevent a clever user from figuring out a way to get to the protected routes. Such security should be implemented on the server. They are instead meant as a way to improve the UX for your apps.

Here are the 4 types of routing guards available:

  • CanActivate: Controls if a route can be activated.
  • CanActivateChild: Controls if children of a route can be activated.
  • CanLoad: Controls if a route can even be loaded. This becomes useful for feature modules that are lazy loaded. They won’t even load if the guard returns false.
  • CanDeactivate: Controls if the user can leave a route. Note that this guard doesn’t prevent the user from closing the browser tab or navigating to a different address. It only prevents actions from within the application itself.

A Simple CanActivate Route Guard

Route guards are most often implemented as classes that implement the needed route guard interface. Let’s see an example with a CanActivate route guard where we ask an auth service if the user is authenticated:

can-activate-route.guard.ts

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

import { AuthService } from './auth.service';

@Injectable()
export class CanActivateRouteGuard implements CanActivate {

  constructor(private auth: AuthService) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
      return this.auth.isUserAuthenticated();
  }
}

Notice how we implement the CanActivate interface by declaring a canActivate method. The method optionally has access to the ActivatedRouteSnapshot and the RouterStateSnapshot, in cases where you would need information about the current route.

In our example the canActivate returns a boolean depending on if the user is authenticated or not, but it could have also returned an observable or a promise that resolve to a boolean.

The CanLoad interface doesn't have as much access into the current router state or activated route.

Using a route guard

In order to use them, route guards should be provided just like services, so in our case let’s add it to our app module’s providers:

app.module.ts

// ...
import { AppRoutingModule } from './app-routing.module';
import { CanActivateRouteGuard } from './can-activate-route.guard';

import { AuthService } from './auth.service';

@NgModule({
  declarations: [
    // ...
  ],
  imports: [
    AppRoutingModule,
    // ...
  ],
  providers: [ AuthService, CanActivateRouteGuard ],
  bootstrap: [AppComponent]
})
export class AppModule { }

And then lastly, you’ll want to add the guard as part of your routing configuration. Here an example with a simple routing module:

app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { HomeComponent } from './home.component';
import { DashboardComponent } from './dashboard.component';

import { CanActivateRouteGuard } from './can-activate-route.guard';

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'dashboard',
    component: DashboardComponent,
    canActivate: [CanActivateRouteGuard]
  }
];

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

Now only users that are authenticated can activate the /dashboard route.

Notice how we provide an array of guards in the route definition. This means that we could specify multiple guards for a single route, and they’ll be evaluated in the order in which they are specified.

Implementing CanLoad and CanActivateChild is done in pretty much the same way.

CanDeactivate

The CanDeactivate guard has a slight difference in its implementation in that we need to provide the component to be deactivated. This allows us to probe the component in question to see if there’s something like unsaved changes:

can-deactivate-route.guard.ts

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

import { DashboardComponent } from './dashboard.component';

@Injectable()
export class CanActivateRouteGuard implements CanDeactivate<DashboardComponent> {
  canDeactivate(component: DashboardComponent): boolean {
    if (component.unsavedChanges) {
      return confirm('Are you sure?');
    }
    return true;
  }
}

In the above example, we assume that there’s a member on the dashboard component class called unsavedChanges that becomes true whenever there are unsaved changes. The route won’t be deactivated unless there are either no unsaved changes or the user confirms.

✖ Clear

🕵 Search Results

🔎 Searching...