November 22, 2019

Complete Guide to Routing in Angular – Part 1

Welcome to Part 1 of Routing in Angular. In this article we are going to learn about routing and how it can be achieved in Angular. To start with, in simple terms, routes allow to change the URL of the application without actually rendering a new page.

Multiple Routes can be defined in an application with each route being associated with an angular component.

Following are the topics which will be covered in this part

  • Creating Routes
  • Using <router-outlet> to render routes
  • Using <routerLink> directive to add links with angular routes
  • Using <routerLinkActive> directive to dynamically apply classes to the active routes
  • Loading routes programatically
  • Adding parameters to routes
  • Accessing the route parameters
  • Adding query parameters to routes
  • Retrieving query parameters from routes
  • Redirecting and Default routes using wildcard routes

Creating the Routing Demo Application

Let us start by creating a application named routing-demo using Angular CLI. Select Yes when the CLI asks if you want to include Routing in the project. This creates a separate Routing Module named AppRoutingModule with all the boilerplate code and the reference in the app.module.ts file as well which are required to register the routes in our application

Also add bootstrap to the application. In case you don’t know refer the link below for the same: https://medium.com/@oyewusioyekunle/how-to-add-bootstrap-to-your-angular-project-angular-8-6379fd6a0f46

Now add two components namely users and departments.

Designing our application

Open app.component.html and add the following code. Here we are creating separate tabs for each of our components.

<div class="container">
    <div class="row">
        <div class="col-md-6 my-3">
            <ul class="nav nav-tabs">
                <li class="active nav-item"><a class="nav-link" href="#">Users</a></li>
                <li class="nav-item"><a href="#" class="nav-link">Departments</a></li>
            </ul>
        </div>
    </div>
    <div class="row">
        <div class="col-md-6">
            <app-users></app-users>
        </div>
        <div class="col-md-6">
            <app-departments></app-departments>
        </div>
    </div>
</div>

So as of now this is our application. We have implemented two tabs but still we can see that content from both are shown. What we actually want to show the content from only the tab which is selected.

Routing-Demo-Initial-Setup

Defining the Routes

We will define our routes in the routes constant of the app-routing.module.ts file.

Now the simplest of routes expects a object with two properties, a path which has a string value with the actual route name and a component which accepts the name of the component we want to render once that route is reached.

Add the below content in the routes constant

const routes: Routes = [
  {
    path:'users' , component:UsersComponent
  },
  {
    path:'departments' , component:DepartmentsComponent
  }
];

Make sure to add the reference for both Users and Departments components.

Using router-outlet to activate the routes

Although we have added the routes but still we have not informed angular where to load these components once the url is as expected. So for this purpose a special directive called <router-outlet> is used.

Whenever angular observers a change in route, it renders the component associated with that route in place where the <router-outlet> directive is defined. Let us now update our app.component.html file to remove calls to <app-users> and <app-departments> with the directive.

<div class="container">
    <div class="row">
        <div class="col-md-6 my-3">
            <ul class="nav nav-tabs">
                <li class="active nav-item"><a class="nav-link" href="#">Users</a></li>
                <li class="nav-item"><a href="#" class="nav-link">Departments</a></li>
            </ul>
        </div>
    </div>
    <div class="row">
        <div class="col-md-6">
            <router-outlet></router-outlet>
        </div>
    </div>
</div>

Now if you navigate to http://localhost:4200/departments or to http://localhost:4200/users you can see that the desired content is getting loaded there. So routing in angular is active and hooked now. But we still need to implement it so that we can have the routes changes on tab clicks.

 Routing-set-up

routerLink Directive

The routerLink directive works similar to the href attribute but without actually reloading the entire page. If you pass the route say “/users” to the href property of anchor tag then the entire page gets refreshed.

But if the routes are passed through routerLink, it first check if a matching route is defined in the system. If it is, then it just renders the component associated with that route and does not reloads the entire page. This actually applies the entire concept of the Single Page Application.

So let us update our code to have the directive point to specific route.

<div class="container">
    <div class="row">
        <div class="col-md-6 my-3">
            <ul class="nav nav-tabs">
                <li class="active nav-item">
                    <a class="nav-link" routerLink="/users" href="#">Users</a></li>
                <li class="nav-item">
                    <a href="#" routerLink="/departments" class="nav-link">Departments</a></li>
            </ul>
        </div>
    </div>
    <div class="row">
        <div class="col-md-6">
            <router-outlet></router-outlet>
        </div>
    </div>
</div>

Now if you click on the Users or Departments tab then the specific component will be loaded and the route can be seen in the URL as well.

routerLinkActive Directive

routerLinkActive is another directive which can be used to indicate which particular tab/route is active. Any class name can be passed to this directive and it will be dynamically applied when that particular anchor or element is in active state.

Let us see that in our application. Update the app.component.html with the following code

<div class="container">
    <div class="row">
        <div class="col-md-6 my-3">
            <ul class="nav nav-tabs">
                <li class="active nav-item">
                    <a class="nav-link" routerLink="/users" routerLinkActive="bg-warning" href="#">Users</a></li>
                <li class="nav-item">
                    <a href="#" routerLink="/departments" routerLinkActive="bg-warning" class="nav-link">Departments</a></li>
            </ul>
        </div>
    </div>
    <div class="row">
        <div class="col-md-6">
            <router-outlet></router-outlet>
        </div>
    </div>
</div>

We have applied the directive on both are anchor tags. So which ever route will be active, that route will have the “bg-warning” class applied to it.

Additionally there is also another directive which is mostly used as an attribute called [routerLinkActiveOptions]. This can be used to pass in some configuration like “{exact:true}” which says that only activate this route when their is an exact match.

Also Read: Create a News App with Angular 8

Loading Routes Programatically

Let us now see how we can activate routes programatically, i.e. through the .ts file say after some processing. For example think of a scenario where we need to redirect user to some list of items once he adds a new item.

Add a new button to the users.component.html file. What we want is to activate the department route on the click of the button.

<p>users works!</p>

<div class="row">
    <div class="col-md-6">
        <button class="btn btn-primary" (click)="loadRoute()">Load Route Programatically</button>
    </div>
</div>

In the users.component.ts file let us now handle this loadRoute() method. We will have to inject Router in our component in order to do so.

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app-users',
  templateUrl: './users.component.html',
  styleUrls: ['./users.component.css']
})
export class UsersComponent implements OnInit {

  constructor(private route: Router) { }

  ngOnInit() {
  }

  loadRoute()
  {
      this.route.navigate(["/departments"]);
  }

}

navigate method of Router is used for this purpose. It takes in the route path and navigates to it. Here once you click on the button the department tab will be activated as we are navigating to it.

Add Parameters to Route

We can use the “:” operator to pass on parameters with are routes. Let us update the routes which we created in app-routing.module.ts to allow the users path to have an “id” parameter as well.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { UsersComponent } from './users/users.component';
import { DepartmentsComponent } from './departments/departments.component';


const routes: Routes = [
  {
    path:'users' , component:UsersComponent
  },
  { 
    path:'users/:id' , component:UsersComponent
  },
  {
    path:'departments' , component:DepartmentsComponent
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],

exports: [RouterModule]
})
export class AppRoutingModule { }

The “:” operator indicates the part following it is dynamic part of the url.

The best method to pass parameters in routes is by using the routerLink directive as an attribute and following the syntax below:

<a class="nav-link" [routerLink]="['/users',10]" routerLinkActive="bg-warning" href="#">Users</a></li>

Update the code in the app.component.html file to pass on the id. Here we are passing the value 10. In case there are multiple parameters then they can be passed similarly.

Now when the Users tab will be clicked then by default id parameter 10 will also be there in the url.

We have hard-coded the id here for demo purpose but in real scenarios it would be the id of an element from the list or the database fetched via Http calls etc.

Accessing the Parameters in Routes

There are two ways in which we can access the parameters from the routes. The first method uses a property called Snapshot which is usefull when you want to get the parameters only once. But in cases when the parameters keep changing the it would be better to utilize the second method which makes use of an Observable which can be subscribed to keep a check on the values of parameter.

Let us update the code in users.component.ts and user.component.html files to get the parameters in both the ways and display it on the screen.

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-users',
  templateUrl: './users.component.html',
  styleUrls: ['./users.component.css']
})
export class UsersComponent implements OnInit {
  snapshotId: number;
  observableId: number;

  constructor(private route: Router,
   private activatedRoute: ActivatedRoute) { }

  ngOnInit() {

    this.snapshotId = this.activatedRoute.snapshot.params['id'];

    this.activatedRoute.params.subscribe(
      (params) => {
        this.observableId = params['id'];
      }
    )

  }

  loadRoute()
  {
      this.route.navigate(["departments"]);
  }

}

Note: ActivatedRoute module needs to be injected in the component in order to access the route parameters.

<p>users works!</p>

<div class="row">
    <div class="col-md-6">
        <p>The ID value passed in the URl by Snapshot = {{snapshotId}}</p>
        <p>The ID value passed in the URl by Snapshot = {{observableId}}</p>
    </div>
    <div class="col-md-6">
        <button class="btn btn-primary" (click)="loadRoute()">Load Route Programatically</button>
    </div>
</div>

Once implemented we can see that originally both will show the same value. But in case the parameter is changed the snapshotId wont be updated since it is a one time assignment while the observableId will be updated as it is subscribed to it for listening changes.

Also Read: Json Web Tokens in Asp.net and Angular

Query Parameters in Angular Routes

To pass on query parameters to the routes we can make use of the special directive called [queryParams] which accepts an KeyValue pair type of object for the query parameter and its value. Modify the code of the link for departments route in app.components.html file to pass on the query parameters.

<a href="#" routerLink="/departments" [queryParams]="{'paramName' :'1'}" routerLinkActive="bg-warning" class="nav-link">Departments</a></li>

When we not navigate to the Department tab we can see the query parameter “paramName” and its value as 1 in the url

Retrieving Query Parameters from Routes

Similar to the normal parameters, query parameters can also be retrieved using the Snapshot or an Observable. Both these functions resides on the queryParams property of the ActivatedRoute class. Let us update the department.component.html and department.component.ts files to log these parameters on screen.

<p>departments works!</p>

<div class="row">
    <div class="col-md-6">
        <p>Snapshot Query Parameter : {{snapshotParam}}</p>
        <p>Observable Query Parameter : {{observableParam}}</p>
    </div>
</div>
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-departments',
  templateUrl: './departments.component.html',
  styleUrls: ['./departments.component.css']
})
export class DepartmentsComponent implements OnInit {
snapshotParam:string;
observableParam:string;

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.snapshotParam = this.route.snapshot.queryParams['paramName'];

    this.route.queryParams.subscribe(
      (queryParam) => {
        this.observableParam = queryParam['paramName'];
      }
    )
  }

}

Redirecting Routes and Wildcard Routes

In the route definition, instead of the component name we can also use the redirectTo property and assign it a value of some other route path on which we want it to be redirected.

Let us add a new route in app-routing.module.ts file with path as “somethingElse” and if this route is reached we want to be redirected to the “users” route.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { UsersComponent } from './users/users.component';
import { DepartmentsComponent } from './departments/departments.component';


const routes: Routes = [
  {
    path:'users' , component:UsersComponent
  },
  { 
    path:'users/:id' , component:UsersComponent
  },
  {
    path:'departments' , component:DepartmentsComponent
  },
  {
    path :"somethingElse" , redirectTo :"/users"
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],

exports: [RouterModule]
})
export class AppRoutingModule { }

Now head over to your browser and manually enter “somethingElse” in the route. It should redirect you to the “users” route.

Another use case similar to this is when user enters a route which has not been defined by you and you want to redirect the user to a custom page. To handle all such request the wildcard route is used which takes in path as “**”. So any route which is not defined will be picked up by this route.

Note: Make sure that the wildcard route is the last route you define otherwise it will ignore all routes below it and you will always be acted by this route.

Let us create a new component called errors and update its Html to say Page Not Found. Now create the wildcard route and define its component to be errors.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { UsersComponent } from './users/users.component';
import { DepartmentsComponent } from './departments/departments.component';
import { ErrorsComponent } from './errors/errors.component';


const routes: Routes = [
  {
    path:'users' , component:UsersComponent
  },
  { 
    path:'users/:id' , component:UsersComponent
  },
  {
    path:'departments' , component:DepartmentsComponent
  },
  {
    path :"somethingElse" , redirectTo :"/users"
  },

  {
    path : "**", component:ErrorsComponent
  }
];

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

Head over to the browser and enter any random text in the route path. You will be taken to the Page Not Found message.

So we have completed the Part 1 of Routing in Angular. In the Part 2 of this series we will look into more complex route topics like Nested Routes and Route Gaurds.

3 thoughts on “Complete Guide to Routing in Angular – Part 1

Leave a Reply

Your email address will not be published. Required fields are marked *