Angular 8 Tutorial

Routing in Angular 8

learn about routing and navigation in Angular 8




A little background

This tutorial on Routing in Angular 8 app is in continuation of our Angular 8 Tutorial Series and if you have not read the previous parts of the tutorial, it is recommended that you go through them first.

Routing in Angular 8

Navigation is at the core of modern web applications. In the digital era most web apps can easily grow from a few hundred to thousands of web pages created dynamically and effective routing is the key to success in such scenarios. In this tutorial , we'll learn how we can add routing to our single page apps written using Angular 8. To achieve our objective , we'll add routing to our InventionsHub app.

Action plan for adding Routes

In order to add routing and navigation mechanism to our InventionsHub app , we'll do the following ::

  1. Add details and id fields to our Invention class
  2. update our inventions.service to reflect above changes and add a method to fetch inventions
  3. Add a new component details.component to display individual invention along with its details
  4. Add a more details button to each of our inventions which if clicked navigates to another component displaying more details about the invention
  5. We'll add routing configuration and router outlets to achieve the above functionality

Edit invention class

Add new fields details and id to the class Invention . Open the file inventions.class.ts and add the following code to it:

                                
// inventions.class.ts
// add a new class 
// add id and details field to the class 
export class Invention {
    id : number ; 
    details : String ;
    name : String ; 
    inventor : String ; 
    year : String ; 
    
}
                                                            
                                
                            

Update invention service

Update inventions service file inventions.service.ts and make it resemble the following:

                            
// inventions.service.ts 

import { Injectable } from '@angular/core';
import { Invention } from './inventions.class'; 


@Injectable()
export class InventionsService {
// updated the mock data about inventions to reflect id and details parameters 
    rawInventions : Invention[] = [
    {
    id : 1 , 
    name : 'Java',
    inventor : 'James Ghosling',
    year : '1995' , 
    details : 'java is an object oriented language '
    } , 
    {
    id : 2 , 
    name : 'Python', 
    inventor : 'Guido van Rosum',
    year: '1991' , 
    details: 'python is simple yet powerful scripting language '
    } , 
    {
    id : 3 , 
    name : 'C++',
    inventor : 'Bjarne Stroustrup',
    year : '1983' , 
    details : ' C++ is the language if you want to make things run faster '
    }
]

    constructor() {
    
    }

    getInventions() : Invention[] {
        return this.rawInventions;     
    }

    
    // added a new method to fetch an invention based on its id 
    inventionDetails( id ) : Invention { 
        for ( var i = 0 ; i < this.rawInventions.length ; i++ ) {
        if ( this.rawInventions[i].id == id )  {
            return this.rawInventions[i]; 
        }
        }
        return null; 
    }


}		
                            
                        

Note the 2 things in the above snippet ::
  1. We added id and details fields to our invention objects
  2. Added a method inventionDetails which takes id as a parameter and returns an Invention object corresponding to that id

Add a new component for details

Now to show details of an invention , lets add a new angular component called details component using the following command:

                
ng g component details
                
            

it creates a directory details along with all the relevant files. Let's edit details.component.ts and add the following code to it:
                
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { InventionsService } from '../inventions/inventions.service'; 
import { Invention } from '../inventions/inventions.class'; 


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

    id : string; 
    invention : Invention; 
    constructor(
    private route: ActivatedRoute,
    private router: Router , 
    private service : InventionsService

    ) { 
    }

    ngOnInit() {
    this.id = this.route.snapshot.paramMap.get('id');
    this.invention = this.service.inventionDetails(+this.id);
    }

    initialize() {
    }

}
                
            

There are quite a few things to note about the above snippet ::
  1. We imported Router, ActivatedRoute to use the routing functionality and access information about currently active route respectively
  2. In the ngOnInit lifecycle callback , we're retrieving the id from the route parameters. We'll see where it came from in the following sections
  3. Also note that we've instantiated an Invention object , this is what we'll render in our view next.
Edit the file, details.component.html to make it look like the following::
            
<div class="container">
    <h2> Details </h2>
    <b> Id   : </b> {{ invention.id }} <br>
    <b> Name : </b> {{ invention.name }} <br>
    <b> Inventor : </b> {{ invention.inventor }} <br>
    <b> Details : </b> {{ invention.details }} <br>
</div>                
            
        

We're now ready to show details about a chosen invention. Now let's go to our inventions component and make the relevant changes there also.
  1. Open the file inventions.component.ts and make the changes as shown below:
                    
    // inventions.component.ts 
    
    import { Component, OnInit } from '@angular/core';
    import { FormsModule }   from '@angular/forms'; 
    import { Invention } from './inventions.class';
    import { InventionsService } from './inventions.service'; 
    import { Router, ActivatedRoute } from '@angular/router';
    
    @Component({
        selector: 'app-inventions',
        templateUrl: './inventions.component.html',
        styleUrls: ['./inventions.component.css'] ,
        providers: []
    })
    export class InventionsComponent implements OnInit {
        nameModel : String; 
        inventorModel : String; 
        yearModel : String; 
        detailsModel : String; 
        inventions : Invention[]; 
        totalInventions : number; 
        
        constructor( 
        private inventionsService : InventionsService , 
            private router: Router 
                ) { 
        this.nameModel = '';
        this.inventorModel = '';
        this.yearModel = '';
        this.detailsModel = '';
        // consuming the service method getInventions() to fetch default inventions 
        this.inventions = inventionsService.getInventions(); 
        this.totalInventions = this.inventions.length; 
        }
    
        ngOnInit() {
        }
    
        createInvention(){
    
        this.totalInventions += 1; 
        let newInvention : Invention = {
            id : this.getId(), 
            name: this.nameModel , 
            inventor : this.inventorModel , 
            year : this.yearModel , 
            details : this.detailsModel 
        };
    
        this.inventions.push( newInvention ); 
        this.nameModel = this.yearModel = this.inventorModel = ''; 
        }
    
        details( id ) {
            this.router.navigate(['/details' , id ]);
        }
    
        getId() { 
        return this.totalInventions ; 
        }
    
    }
                    
                

    Points to note about the above snippet ::
    1. We imported the routing related module as we did above
    2. We added id and details parameters to the invention objects
    3. We added the details function , which routes to the details component where we display details about the chosen invention.
  2. Now let's move on to the html file of this component i.e. inventions.component.html and make the following changes:
                    
    <!-- bind data using curly braces to the data in app.component.ts -->
    
    <!-- comment our the data related to single invention we don't need it because 
        we're using array now  -->
    <!-- 
    <h2> {{ invention.name }} </h2>
    <h3> {{ invention.inventor }} </h3>
    <h3> {{ invention.year }} </h3>
    
    -->
    
    <!-- use *ngFor to iterate through array of inventions and print information about them -->
    
    
    
    <div *ngFor="let i of inventions">
        <div class="invention">
            <span> <b> Name: </b>  {{ i.name }}  || </span>
            <span>  <b> Inventor: </b>  {{ i.inventor }}  || </span>
            <span>  <b> Year: </b> {{ i.year }}  </span>
            <span> <button class="btn btn-primary" (click)="details(i.id)"> details </button> </span>
        </div>
    </div>
    
    
    
    
    
    <!--  add new inventions -->
    
    <hr>
    
    
    <!-- Let the user enter information and display the same side by side -->
    
    <!-- <label> {{ nameModel }} </label> -->
    <div class="invention-creator">
        <div>
            <input [(ngModel)]="nameModel" placeholder="enter name" class="form-control">
        </div>
        <hr>
        <!-- <label> {{ inventorModel }} </label> -->
        <div>
            <input type="text" [(ngModel)]="inventorModel" placeholder="enter inventor " class="form-control">
        </div>
        <hr>
        <!-- <label> {{ yearModel }} </label> -->
        <div>
            <input [(ngModel)]="yearModel" placeholder="year" class="form-control">
        </div>
        <hr>
        <div>
            <textarea type="textarea" rows="4" cols="20" [(ngModel)]="detailsModel" placeholder="details" class="form-control">
            </textarea>
        </div>
        <br>
    
        <!-- call the createInvention() function from component on click of button -->
        <!-- note the use of parenthesis for the click event -->
        <button class="btn btn-primary" (click)="createInvention()"> create invention </button>
    
    </div> 
        
                    
                

Great ! now our app is ready to support routing , lets add the routes and routing out-lets so the components can be rendered.

Add Routing config

Now open the file app.module.ts and add the routing imports and configuration as following:

                
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule }   from '@angular/forms'; 
import { AppComponent } from './app.component';
import { InventionsComponent } from './inventions/inventions.component';
import { InventionsService } from './inventions/inventions.service'; 
import { RouterModule }   from '@angular/router';
import { DetailsComponent } from './details/details.component'; 

@NgModule({
    declarations: [
    AppComponent,
    InventionsComponent,
    DetailsComponent
    ],
    imports: [
    RouterModule.forRoot([
    {
    path: '',
    redirectTo: 'inventions',
    pathMatch: 'full'
    },
    {
    path: 'inventions',
    component: InventionsComponent
    } , 
    {
    path: 'details/:id' , 
    component: DetailsComponent
    } 
]),
    BrowserModule , 
    FormsModule
    ],
    providers: [ InventionsService ],
    bootstrap: [AppComponent]
})
export class AppModule { }
        
                
            

Note that how we've mapped urls to the corresponding components to be rendered. Now we need to define router outlets where our components will be rendered.

Router outlets

Right from beginning we've been using app.component as a container, and now it's clearly visible why we did that. Our app.component will act as a container component and this is where we'll add outlets for our routes. Open the file app.component.html and edit it to resemble the following:

                
<!-- app.component.html -->

<div class="container">

    <div style="text-align:center">
        <h1>
            Welcome to {{title}}!!
        </h1>

    </div>
    &nbsp; &nbsp; || &nbsp; &nbsp;
    <a routerLink="/inventions"> Inventions </a> &nbsp; &nbsp; || &nbsp; &nbsp;


    <hr>

    <!-- <app-inventions> </app-inventions> -->
    <router-outlet></router-outlet>


</div> 
                
            

Note that we've declared the outlets where our active components will render. we've also added a router-link which helps us to navigate back to the inventions component.
OK now , everything's setup and our app is all set up with routing and navigation. Just serve the app locally using ng serve and go to your browser and http://localhost:4200 . Following are the screenshots taken from our InventionsHub app.
Ng serve successfull


Ng serve successfull

Summary

Let's see what we learned in this tutorial on navigation and routing in Angular 8 apps ::

  1. Setting up Routing Configuration in Angular 8
  2. Making use of router-outlets in Angular 8
  3. Using the router-links in Angular 8
  4. Passing parameters to angular routes while calling them
  5. Retrieving the passed parameters
  6. Apart from these we used our previous knowledge on angular components , services etc to make our app use routing
Hope you enjoyed learning about routing and navigation in Angular 8. Please post any questions in comments.