Routing and Navigation

( 0 users )

Routing is a functionality that enable us to move from one view to another. Angular Router does this for us. In a Web Application, we generally do navigation by entering URL, click of hyper-links and buttons, dynamic navigation through JavaScript etc.

The Angular Router wraps all these instructions and provide us with seamless functionalities that, as a developer, we can implements in most easy and robust way to provide routing features in our Angular application.

How Angular Router works

  • Every view is associated with a component. The first step is to configure routes for our view components.
  • We define some router options on the component.
  • We define router actions on the component.
  • We activate the route when user does the navigation or any routing action.
  • When a route gets activated for a component, it displays the view of that component.

Importing and Configuring Routes

In your 'app.modules.ts', import RouterModule from '@angular/router'.


import {RouterModule} from '@angular/router'

@NgModule({
  imports: [ BrowserModule, RouterModule.forRoot([]) ],
  declarations: [ App ],
  bootstrap: [ App ]
})
export class AppModule {}		
		

This will register the Angular router provider. The forRoot([]) method signifies that the routes are available to your root application module. By default HTML5 URLs are configured for routing, if you want to use Hash(#) style routing like Angular 1 provided, then add an option to the forRoot() method.


RouterModule.forRoot([], {useHash : true})		
		

Example Application

Let's make our example application which will make use of Angular Router and will show how the routes are configured, activated and used. We are making a sample grocery app which will show different category of food products and user can click on each category to navigate to its view.

Creating food category component classes

Let's create three food category components - Fruits, Vegetables and Snacks. This is our Fruits component class:


import {Component} from '@angular/core'

@Component({
  template: `
 <h2>Fruits</h2>
    <p>Choose fruits</p>
    <select>
      <option>Apple</option>
      <option>Banana</option>
      <option>Oranges</option>
      <option>Grapes</option>
      <option>Pomgranates</option>
    </select>
  `
})

export class Fruits {}			
			

The other two component classes - Vegetable and Snacks are similar at this moment. We will later modify our Snacks component to illustrate more routing examples.

Configuring Routes

In our main application component, we will add a navigation bar and add routing links to Fruits, Vegetables and Snacks components:


import {RouterModule} from '@angular/router'
import {Fruits} from './fruits'
import {Vegetables} from './vegetables'
import {Snacks} from './snacks'

Component({
  selector: 'my-app',
  template: `
 <div><h1>Select Food Category</h1></div>
    <nav><h3>
      <a [routerLink]="['/fruits']" routerLinkActive="active">Fruits</a>
      <a [routerLink]="['/vegetables']" routerLinkActive="active">Vegetables</a>
      <a [routerLink]="['/snacks', '1']" routerLinkActive="active">Normal Snacks</a>
      <a [routerLink]="['/snacks', '2']" routerLinkActive="active">Fat Free Snacks</a>
    </h3></nav>
    <router-outlet></router-outlet>
  `,
})
export class App {  
  constructor() {
   
  }
}

const appRoutes: Routes = [
  { path: 'fruits', component: Fruits },
  { path: 'vegetables', component: Vegetables },
  { path: 'snacks/:id', component: Snacks },
  { path: '',   redirectTo: '/', pathMatch: 'full' },
  { path: '**', component: PageNotFound }
];		
		

Ok, so let's deep dive into the above code

First, see this line of code

<a [routerLink]="['/fruits']" routerLinkActive="active">Fruits</a>

Here, we are telling angular that to link a route './fruits' in its current route. The routeLinkActivate tells Angular that we need to activate this route. We can not navigate to a route without activating it. This is to make Angular routing more flexible.
We can also pass parameters to a particular route.

<a [routerLink]="['/snacks', '1']" routerLinkActive="active">Normal Snacks</a>

If you see, we are passing second item to the array of routerLink. This item will be passed in the URL like /snacks/1 and will be provided to the target component class which is configured for this route. We will see later how we consume this parameter in the router component class.
Suppose, we have entered just the site's address without any route. Then we sometimes need to redirect to some other route, may be a login page or some other welcome page. This is how it is done.

{ path: '',  redirectTo: '/', pathMatch: 'full' }

pathMatch full - It indicates the Router to match the path only when the path is '/' is there and not './'. If nothing matches, the a default path matches which is **

{ path: '**', component: PageNotFound }

The <router-outlet> directive let's the component's view display here. Path match properties matches the path in a sequence, so any path that matches first will be considered for routing.
You need to add <base href="/"> after head opening tag in index.html to let Angular know as how to construct the paths. All files, js, html, css will then reference with this path.
This is useful when we have multiple applications hosted in our server. In this case we can give application name here. For eg. <base href="/PhotoViewer"> Then the URL will be http://www.example.com/PhotoViewer/. All other router paths will be composed to this path.

Route Parameters

Till now we have configured routes. As mentioned above, we need to consume our Snacks component route parameter. This is how we do it.


import {ActivatedRoute} from '@angular/router'
import {Observable} from 'rxjs/Observable'
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/observable/of';

export class Snacks implements OnInit{
  
snackType:number;
constructor(private _route : ActivatedRoute){

}

ngOnInit(){
 this._route.paramMap
 .switchMap((params: ParamMap) => {
  return Observable.of(params.get('id'));
})
.subscribe(id => {this.snackType = +id;});

}		
		

We first import the ActivatedRoute from '@angular/route' and inject it into our constructor. ActivatedRoute provide us with the current route information (path, URL, parameters etc). The ActivatedRoute paramaMap property has one method get which helps to get the named parameter from the URL. One thing to note that we are observing the params.get('id') value. This is required when we want to navigate to same route but with different route parameter.

For e.g. if we are navigating from /snacks/1 to /snacks/2, since the component is same so ngOnInit is not going to be called again. So we starts observing on the parameter itself as soon as the component gets rendered.

In the subscribe function, we are just assigning our snackType property to the route parameter. In our template view, we utilize this property as:

Snacks | {{snackType == 1? 'Normal' : 'Fat free'}}

Note : RxJS is a separate topic that is something not going to be covered here. Please refer - http://reactivex.io/rxjs for detailed documentation.

Protecting the Routes - Route Guards

There are several time when we want user to restrict to some routes. It can be one of the following scenarios:

  • User should not access the app before login, so we need to deny access to main application routes.
  • Some users may be administrators, some normal users or some may be guest users depending on the application's user access layer. So we need to restrict access to certain routes accordingly.
  • If some http response is in progress, you might not want the user to navigate to some other route. It can be the case where user save a form and it is still in progress.
  • You might want to pre-fetch some data before actually displaying the component, may be to check some validation. In this case too, you need to protect that route.

The router supports multiple guard interfaces:
CanActivate - intercept navigation to a route.
CanDeactivate - intercept navigation away from the current route.
Resolve - pre-fetch data before route activation.
CanLoad - intercept asynchronous routing

Continuing with our example, we will now make a service RouteGuardService


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

@Injectable()
export class RouteGuardService implements CanActivate {
  
  canActivate(route: ActivatedRouteSnapshot): boolean {
    let snackTypeId = route.url[1].path;
    if(snackTypeId == 2){
      alert('Not available for guest users!');
      return false;
    }
    return true;
  }
}		
		

We are importing CanActivate and ActivatedRouteSnapshot. We implement the CanActivate interface which has one method to implement canActivate() (remember it intercepts navigation to route). With this, we inject ActivatedRouteSnapshot to get the current activated route information. Here ,we are just checking the snacks route parameter and if it is 2, we are showing an alert. Now, we need to do some changes in our main application module. First import the RouteGuardService.

import {RouteGuardService} from './routeguardservice'

Then replace

{ path: 'snacks/:id',  component: Snacks }

With

{ path: 'snacks/:id', canActivate : [RouteGuardService], component: Snacks }

See that we are passing one more option canActivate and assigning it to an array. This array contains the CanActivate implemented Service, which will be invoked every time the Angular Router hits this path.

Summary

So we have learnt several things about Angular routing and navigation:

  • What is Angular Routing and how it works.
  • Configuring routes.
  • Using route parameters.
  • Protecting routes with Route Guards.

For in-depth documentation please refer Angular official documentation - https://angular.io

Full working source code of this example is available here - https://stackblitz.com/edit/angular-routing-and-navigation?embed=1&file=src/app/app.component.html&hideNavigation=1&view=preview

To Do

* Note : These actions will be locked once done and hence can not be reverted.

1. Track your progress [Earn 200 points]

2. Provide your ratings to this chapter [Earn 100 points]

0