In Part I of this guide, I discussed designing the architecture for our fictitious app The Snap Gram. Next, we will take a deeper look at implementing routes in an Angular application. I consider this Angular's strong suit — creating single page applications. If you want to follow along with the examples, you can download the boilerplate code from Angular's Quickstart project. We will create an app with a few pages to demonstrate how routing works.

The Basics

These are the essential files we will be using:

project/ 
|__package.json
|__node_modules/
|__src/
     |__index.html
     |__main.ts
     |__app/
          |__app.module.ts
          |__app.component.ts
          |__app.component.html
          |__app-routes.module.ts
          |__not-found.component.ts
          |__not-found.component.html
          |__user.component.ts
          |__user.component.html
          |__user-detail.component.ts
          |__user-detail.component.html          

index.html

The main view for the app. It is where all other views will be injected. Example:

<!DOCTYPE html>
<html>
  <head>
    <base href="/">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Angular QuickStart</title>
    <link rel="stylesheet" href="styles.css">
    <!-- Polyfill(s) for older browsers -->
    <script src="node_modules/core-js/client/shim.min.js"
    </script>
    <script src="node_modules/zone.js/dist/zone.js"></script>
    <script src="node_modules/systemjs/dist/system.src.js">
    </script>
    <script src="systemjs.config.js"></script>
    <script>
      System.import('main.js').catch(function(err){ console.error(err); });
    </script>
  </head>
  <body>
    <my-app></my-app>
  </body>
</html>

Notice in the head of the document the element <base href="/">. This sets the base URL for our app. / is the root. If we changed it to <base href="/posts"> all of our routes would begin with /posts. The next important note is this line: <my-app></my-app>. This element was created in the root component file. We could have named it anything we wanted. It is where the view for our different routes will be inserted. Let us create our root component.

app.component.ts

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
})
export class AppComponent  { name = 'My Website Title'; }

The templatetUrl links to the view we will use as a parent view for all other views to inherit.

app.component.html

<header>
   ...
</header>
<router-outlet></router-outlet>
<footer>
  ...
</footer>

Views we define for our routes will be placed after <router-outlet></router-outlet>. The next thing to do is to add the root component to the root module.

app.module.ts

import { NgModule }      	from '@angular/core';
import { BrowserModule } 	from '@angular/platform-browser';

import { AppComponent }  	from './app.component';

@NgModule({
  imports: [ BrowserModule ],
  declarations: [ AppComponent ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

At this point, if you start your app, you will only see a blank screen except for anything you put in the header and footer. Let us configure our routes to add some pages. We will put all of the routes in their own module, so it does not clutter the root module.

app-routing.module.ts

import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { AppComponent }         from './app.component';
import { UserComponent }        from './user.component';
import { NotFoundComponent }    from './not-found.component';


const routes: Routes = [
	{ path: '', redirectTo: '/users', pathMatch: 'full'},
	{ path: 'users', component: UserComponent },
	{ path: '**', component: NotFoundComponent }
]

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

The router module is needed to configure our routes. In our list of route definitions, we are redirecting routes from the root path to the users path. The ** path will match any path that we have not listed before it. We use it to send all undefined routes to our 404 page. The routes are configured with the statement RouterModule.forRoot(routes).

This is an example of a template for the user component.

user.component.html

<section>
    <div class="container">
        <h2>{{ title }}</h2>
        <ul>
            <li *ngFor="let user of users">
               {{ user.username }}
            </li>
        </ul>
    </div>
</section>

Reader's TODO

Implement user.component.ts and the NotFoundComponent.

Implementing Routing

Next, we need to include the routing module in our app root module for it to work. This is what the updated app.module.ts looks like now:

import { NgModule }          from '@angular/core';
import { BrowserModule }     from '@angular/platform-browser';

import { AppComponent }      from './app.component';
import { UserComponent }     from './user.component';
import { NotFoundComponent } from './not-found.component';
import { AppRoutesModule }   from './app-routes.module';

@NgModule({
  imports: [ 
  	BrowserModule, 
  	AppRoutesModule,
  ],
  declarations: [ 
  	AppComponent, 
  	UserComponent, 
  	NotFoundComponent
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

We have imported the routing module and added it to the imports array. We have also imported the users component and the not found component and added them to our list of declarations. Normally, I would create a module for users, and the user component would be declared in that file. However, for the purpose of this tutorial, I am including it in the root module.

Try navigating your browser to the root path. You should see the URL change and the user view appear. Now try navigating to a URL you did not configure. Your 404 page should show.

Next, we will create a parameterized route. Parameters give us a way to define multiple routes that follow a particular pattern. For example, if we want to create routes for each user we could use the parameter :id to link to a post by its id attribute. We could have chosen any name for our parameter. All that is important is that it is prepended with a colon.

Create a component for this route and add the route definition to our list of routes. This is the updated app-routes.module.ts file.

import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { AppComponent }         from './app.component';
import { UserComponent }        from './user.component';
import { UserDetailComponent }  from 'user-detail.component';
import { NotFoundComponent }    from './not-found.component';


 const routes: Routes = [
    { path: '', redirectTo: '/users', pathMatch: 'full'},
    { path: 'users', component: UserComponent },
    { path: 'users/:id', component: UserDetailComponent },
    { path: '**', component: NotFoundComponent }
]

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

UserDetailComponent will need to be in the root module and added to the list of declarations. Here is the updated app.module.ts file.

import { NgModule }          from '@angular/core';
import { BrowserModule }     from '@angular/platform-browser';

import { AppComponent }      from './app.component';
import { UserComponent }     from './user.component';
import { UserDetailComponent } from './user-detail.component';
import { NotFoundComponent } from './not-found.component';
import { AppRoutesModule }   from './app-routes.module';

@NgModule({
  imports: [ 
  	BrowserModule, 
  	AppRoutesModule,
  ],
  declarations: [ 
      AppComponent, 
      UserComponent, 
      UserDetailComponent,
      NotFoundComponent
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

Reader's TODO

Create the UserDetailComponent class and template.

Implementing Navigation

Next, we need links to navigate to these routes. For that, we will attach the routerLink directive to our link element. Update the app.component.html to provide a link to the user’s page in the navigation:

<a routerLink="/users">Users</a>

In user.component.html add links to each list element.

<li *ngFor="let user of users">
    <a [routerLink]="['/users', user.id]">
        {{ user.username }}
    </a>
</li>

Linking to a route that uses a parameter is a little different. The routerLink directive is wrapped in brackets because it is now being bound as a property to our element whose value is "['/users', user.id]". The first part of this expression is the base path and the second part is the value of the parameter.

Summary

Here is a recap of the key parts to routing:

  • <base href="/"> - Put in the head of the index file to establish the base path for URLs to be mounted.
  • <router-outlet></router-outlet> - Used in the root component where all routed views will be placed.
  • RouterModule.forRoot(routes) - Configures the routes we defined in a routes array.
  • path/:param - Syntax for defining a route with a parameter.
  • routerLink - Directive to add to elements to link to a path.