Angular 2 Routing

Tags: Angular

In the previous post I went step-by-step to create an Angular 2 "Hello World" application. The code for that post is available on GitHub here. In this post I want to extend the "Hello World" app and look closer at the Angular routing in there.

The Angular Router (or just "router") uses a convention-based approach to match the browser URL to the various views in our application. As users click on navigation buttons or anchor tags the router will redirect users to the client view and even pass in optional parameters. Last, the router logs all navigation with the browser so that the user can click forward and backward buttons in the browser and our application will still work.

Step 1 is adding the base href tag to our index.html page:

app.routing.ts

Now we should import (reference) the router itself and declare the routes. In the app folder where app.module.ts lives add a new file called app.routing.ts:

import { RouterModule, Routes } from '@angular/router';
import { HomeComponent }     from './home/home.component';
import { AboutComponent } from './about/about.component';

const routes: Routes = [
  { path: '',  pathMatch:'full', redirectTo: '/home' },
  { path: 'home',  component: HomeComponent },
  { path: 'about', component: AboutComponent }
];

export const AppRouting = RouterModule.forRoot(routes);

In this example I've added an about page. Notice that declarative routing requires both the path and component name. The forRoot function bootstraps our routes array when the application starts up. The router takes care of this for us.

Router Outlet

we have a single index.html page and multiple components. So redirects are not going to different html pages. Instead Angular 2 requires a region in the host view in which all routed views will be redirected. The template we defined in app.component.ts is the host view. That's where we need to put the router-outlet tag. I've also put our menu with two links there as well:

app.component.html

<div class="container">
    <nav class="navbar navbar-default">
        <div class="container-fluid">
            <a class="navbar-brand">{{applicationName}}</a>
            <ul class="nav navbar-nav">
                 <li role="presentation" class="active">
                     <a [routerLink]="['/home']">Home</a>
                 </li>
                 <li role="presentation">
                     <a [routerLink]="['/about']" >About</a>
                 </li>
            </ul>
        </div>
    </nav>
    <div class="container">
        <router-outlet></router-outlet>
    </div>
</div>

The nav region is our header with a one-way binding to the application name and the div tag below it is our router-outlet region where all routed views will be injected.

app.component.ts

Modify the app.component.ts to provide the applicationName property so that Angular can bind the value at runtime:

import { Component } from '@angular/core';

@Component({ 
  moduleId: module.id,
  selector: 'app-container',
  templateUrl: 'app.component.html'
})
export class AppComponent {
  constructor() { } 
  applicationName: string = "Angular 2 Routing";
}

Now we've got to add the about page. Under the app folder add a new subfolder called about with these two files:

about.component.ts

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

@Component({
    moduleId: module.id,
    selector: 'about',
    templateUrl: 'about.component.html'
})
export class AboutComponent implements OnInit {
    constructor() { }
    ngOnInit() { }
}

about.component.html

<h3>About</h3>
<p>
    About us goes here. 
</p>

Our main module needs to know about the new component. So import and declare it there:

app.module.ts

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule} from '@angular/http';

import { AppComponent }  from './app.component';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
import { AppRouting } from './app.routing';

@NgModule({
  imports:      
  [ 
    BrowserModule, 
    FormsModule, 
    HttpModule, 
    AppRouting 
  ],
  declarations: 
  [ 
    AppComponent, 
    HomeComponent,
    AboutComponent
  ],
  providers:    [ ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

Start the app and you'll see the application name and Bootstrap container-fluid menu layout:

Route Parameters

This is a basic menu routing system without parameter declarations. But suppose we had a component that had a table of widgets in which the user would click one and go to a specific widget? The syntax is straightforward. Let's add a hypothetical widget component to our router.

Clone the about folder and rename everything to widget. Add the component to the app.module.ts and update the app.routing.ts route array. It should look like this:

app.routing.ts

import { RouterModule, Routes } from '@angular/router';
import { HomeComponent }     from './home/home.component';
import { AboutComponent } from './about/about.component';
import { WidgetComponent } from './widget/widget.component';

const routes: Routes = [
  { path: '',  pathMatch:'full', redirectTo: '/home' },
  { path: 'home',  component: HomeComponent },
  { path: 'about', component: AboutComponent },
  { path: 'widget/:id', component: WidgetComponent }
];

export const AppRouting = RouterModule.forRoot(routes);

Notice that the colon symbol (:) tells the router that there is a parameter. Now on the about.component.html file add a link to test the route:

<h3>About</h3>
<p>
    About us goes here. 
</p>

<p>
    <a [routerLink]="['/widget/42']" >Go to Widget 42</a>
</p>

Save your changes and start up the app. Click the link on the about page and the router will redirect you to the widget view with the parameter value: