Getting Started With the New Angular 2 Router

Navigation has changed a lot during Angular 2's development. Finally this important piece has become final!

Today I'll show you how to master routing into your Angular 2 apps. We will create our first routes, protect them, handle an event when the user leaves and create some children routes.

Let's create some routes

First, let's start by adding the RouterOutlet into our index.html, this is the equivalent of ng-view from Angular 1, every views that we create will be displayed here:

< router-outlet > </ router-outlet >

We then create a Home Component (a dry version of the Angular Webpack Starter's one):

import { Component } from "@angular/core" ; @ Component ( { selector : "home" , template : "Welcome Home" } ) export class HomeComponent { ngOnInit ( ) { console . log ( "hello `Home` component" ) ; } }

Best Practice is to create an app.route.ts and to put the routes there:

import { Routes } from "@angular/router" ; import { HomeComponent } from "./home" ; import { NoContentComponent } from "./no-content" ; export const ROUTES : Routes = [ { path : "" , component : HomeComponent } , { path : "home" , component : HomeComponent } , { path : "**" , component : NoContentComponent } ] ;

We create three paths, one for the home, another when the user doesn't type a path (only the hostname) and the last one to handle unknown paths.

After that, we need to get those routes to our bootstrap file, I'm using the Angular Webpack Starter so in my case it will be the app.module.ts, here are the required modifications:

import { RouterModule , PreloadAllModules } from '@angular/router' ; import { ROUTES } from './app.routes' ; import { HomeComponent } from './home' ; import { NoContentComponent } from './no-content' @ NgModule ( { declarations : [ HomeComponent , NoContentComponent ] , imports : [ RouterModule . forRoot ( ROUTES , { useHash : true , preloadingStrategy : PreloadAllModules } ) ] } )

Same logic for declaring our Home and NoContent Components.

Finally, we add our routes using RouterModule.forRoot.

We use useHash and preloadingStrategy, we will have an in-depth look at this in another post but basically this means using the HTML 5 hashbang mode and loading every modules as quick as possible.

That's it! We just created our first routes.

Preventing Unwanted Accesses

Now let's prevent unwanted users from accessing our home by using Route Guards.

A flashy name indeed but not very hard to setup.

We create a service for this that we name ActivateGuard.

First the index.ts file:

export * from "./activate-guard.service" ;

Then the activate-guard.service.ts file:

import { CanActivate } from "@angular/router" ; export class ActivateGuard implements CanActivate { canActivate ( ) { return false ; } }

The most important thing here is the CanActivate Interface.

The canActivate method is where you will write your code to block unwanted users access (Login check, Country Check, Device Check, etc). For this example, we will just return false every time.

Our Guard is ready to be used now, back to the app.route.ts, we just need to add our Guard like this:

import { ActivateGuard } from './guard' ; export const ROUTES : Routes = [ . . . { path : 'home' , component : HomeComponent , canActivate : [ ActivateGuard ] } , . . . ]

Finally in our app.module.ts we import our ActivateGuard and add it to the providers otherwise Angular will never find it:

import { ActivateGuard } from './guard' ; const APP_PROVIDERS = [ ActivateGuard ] ; @ NgModule ( { . . . providers : [ APP_PROVIDERS ] . . . } ) ;

Now nobody can access our Home Component. Great Success!

Handling Users Leaving

CanDeactivate is my favorite feature that the Angular 2 Router added. It gives us the possibility to launch an action before the user leaves the view, you can realise actions like opening a confirm modal, closing a modal, sending a request to the db and much more!

This feature is very similar to canActivate. We are going to create here a DeactivateGuard Service.

First updating our Guard's index.ts:

export * from "./activate-guard.service" ; export * from "./deactivate-guard.service" ;

Then we create the DeactivateGuard Service:

import { CanDeactivate } from "@angular/router" ; import { HomeComponent } from "../home" ; export class DeactivateGuard implements CanDeactivate < HomeComponent > { canDeactivate ( component : HomeComponent ) { return window . confirm ( "Are you sure that you want to leave?" ) ; } }

Here before leaving the view, we just pop a confirm window for user. Note that the canDeactivate feature requires the component (here the Home Component).

Add it to the routes:

import { ActivateGuard , DeactivateGuard } from './guard' ; export const ROUTES : Routes = [ . . . { path : 'home' , component : HomeComponent , canActivate : [ ActivateGuard ] , canDeactivate : [ DeactivateGuard ] } , . . . ]

And add it to the app.module.ts:

import { ActivateGuard , DeactivateGuard } from './guard' ; const APP_PROVIDERS = [ ActivateGuard , DeactivateGuard ] ; @ NgModule ( { . . . providers : [ APP_PROVIDERS ] . . . } ) ;

Children Routes

Until now we have only added routes into the app.routes file however it's more modular to have your routes separated into their own modules folders.

Let's create some children routes for a new Module: Profile. Following the naming convention, this folder will be named "+profile" in order to recognise that it contains children routes.

In this folder, the index.ts that will contain the route and the Component's declaration looks will be:

import { NgModule } from "@angular/core" ; import { RouterModule } from "@angular/router" ; import { ProfileComponent } from "./profile.component" ; export const routes = [ { path : "create" , component : ProfileComponent } , { path : ":id" , component : ProfileComponent } ] ; @ NgModule ( { declarations : [ ProfileComponent ] , imports : [ RouterModule . forChild ( routes ) ] } ) export default class ProfileRoutingModule { static routes = routes ; }

We create two routes here, one with a param named "id" (profile/userId1 for example) and the second one with a sub-path named "create" (profile/create).

The order of the routes is very important, if we put the ":id" route declaration before the "create" one, Angular will consider that "create" is the ":id" parameter, that's why the "create" route declaration is done first in order to avoid confusion.

Always declare the routes from the most specific to the more generic.

Keep in mind the ProfileRoutingModule Class that is exported by default with the routes.

Finally, in app.routes.ts, we declare that everything about Profile need to be loaded asynchronously (loadChildren) using the folder "+profile" and the routes from the component exported by default (ProfileRoutingModule):

export const ROUTES : Routes = [ { . . . { path : 'profile' , loadChildren : ( ) = > System . import ( './+profile' ) . then ( ( comp : any ) = > comp . default ) } , . . . ] ;

And Voila! We have our children routes into another module!

Now that we got all our views ready, those links allow us to navigate to them:

< a [ routerLink ] = " ['./home'] " > Home < / a > < a [ routerLink ] = " ['./profile', 1] " > Profile 1 < / a >

Conclusion

In this post, we have seen how to declare our routes into the app.routes.ts, how to prevent unwanted accesses using canActivate coupled with an ActivateGuard Service, one of my favorite feature canDeactivate which gives us one last chance to act before the user leaves the view and finally exporting our routes into separate modules with asynchronously loading components.