In this tutorial, we’re going to talk about Angular 2 router. We’ll implement a basic Angular 2 application that uses routing to simulate a static site with a navigation menu and a few main sections.
In a static site, the URI generally represents a path on a server. In basic scenario, when user navigates to:
http://static.mycoolsite.com/home/
The server interprets the incoming request and sends the contents of /home folder (most likely index.html
) residing somewhere on the server. This almost always requires a page refresh. But when developing SPAs and web applications in general, we want to make as few page refreshes as possible.
However, if we keep the browser sitting on a single URI, we lose a lot of useful and familiar functionalities that browsers provide. Namely the ability to use the history and to navigate to specific points in our app.
How can we address this problem and keep a functioning URI system while limiting the amount of complete refreshes? Angular Router was designed to address this problem - it parses the URI and activates the components associated with a particular route.
Routing app overview
Let’s take a look at our sample Angular 2 application. Here we have a project with a structure that follows the official documentation guidelines.
app/
...app.component.ts
...home.component.ts
...about.component.ts
...contact.component.ts
...app.module.ts
...app.routing.ts
...main.ts
dist/
node_modules/
typings/
index.html
package.json
systemjs.config.js
tsconfig.json
If you’re used to Angular 1.X, this may seem a bit overwhelming at first. Gone are the days of simply including the angular.js
file and be ready to go. Angular 2 is more complex in structure and uses npm modules (node_modules/
) registered in package.json
. To use them in the browser, we have to use a module loader like SystemJS
that is configured via systemjs.config.ts
file. In app/
folder we have our Typescript files that are compiled to Javascript and put in dist/
folder (configured in tsconfig.json
). Typescript declaration files reside in typings/
directory. index.html
serves as entry point to our application.
Making our app ready for routing
Creating the components
To get our application working, we have to create components for each section.
They will be loaded into the view with the help of a router-outlet component. We’ll try to keep those components as simple as possible. They consist of a minimum boilerplate code and a template that serves as content for the sections of our application.
First, we have a component for the “home” page:
// home.component.ts
import { Component } from '@angular/core';
@Component({
template: `
<div class="jumbotron">
<h2>Hello Angular 2 Routing!</h2>
</div>
`
})
export class HomeComponent {
}
Then for an “about” page:
// about.component.ts
import { Component } from '@angular/core';
@Component({
template: `
<div class="jumbotron">
<h2>About page</h2>
</div>
`
})
export class AboutComponent {
}
And finally, a simple “contact” page:
// contact.component.ts
import { Component } from '@angular/core';
@Component({
template: `
<div class="jumbotron">
<h2>Contact page</h2>
</div>
`
})
export class ContactComponent {
}
Setting the base tag
To get our application ready to make use of routing we must set our base tag. It tells the browser which base URI to use for relative URI paths. Angular 2 documentation states that it should be placed as the first child of the
tag of ourindex.html
. This path directly relates to our /app
folder.
<base href="/">
Creating routes
Now that the components are ready we can start wiring them up to our routes. Here’s what our finished routes file looks like.
// app.routes.ts
import { Routes, RouterModule } from '@angular/router';
import { ModuleWithProviders } from '@angular/core';
import { HomeComponent } from './home.component';
import { AboutComponent } from './about.component';
import { ContactComponent } from './contact.component';
const appRoutes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: 'contact', component: ContactComponent }
];
export const appRoutingProviders: any[] = [
];
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);
In our case we’ll have three routes:
http://localhost:3000/
http://localhost:3000/about
http://localhost:3000/contact
After the necessary import statements, we define an array of Route objects. It tells the router which component should be associated with the given URI path. HomeComponent
will serve as the default path, and we’ll use AboutComponent
and ContactComponent
as two navigable sections.
const appRoutes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: 'contact', component: ContactComponent }
];
The Router module is created by passing an array of Routes to the forRoot()
function exposed by the RouterModule
.
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);
We then make our routes available by exposing the routing
module.
Getting AppComponent
ready for routing
Our container component, AppComponent
, will house our entire application. We’ve created our navigation menu and added some Bootstrap to make things easier on the eye.
// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'router-app',
template: `
<header>
<nav class="navbar navbar-inverse">
<div class="navbar-header">
<a href="/" class="navbar-brand">Angular 2 Routing</a>
</div>
<ul class="nav navbar-right navbar-nav">
<li>
<a routerLink="/" routerLinkActive="active">Home</a>
</li>
<li>
<a routerLink="/about" routerLinkActive="active">About</a>
</li>
<li>
<a routerLink="/contact" routerLinkActive="active">Contacts</a>
</li>
</ul>
</nav>
</header>
<main>
<router-outlet></router-outlet>
</main>
<footer class="text-center">
Copyright © 2016
</footer>
`,
styles: [`
.navbar-right { margin-right: 5px; }
.navbar-right li { cursor: pointer; }
`]
})
export class AppComponent {
}
We have inserted a router-outlet component in the AppComponent
template. This is where our other component templates will be rendered.
<main>
<router-outlet></router-outlet>
</main>
Let’s look at how routing is implemented in our anchor tags.
<a routerLink="/" routerLinkActive="active">Home</a>
<a routerLink="/about" routerLinkActive="active">About</a>
<a routerLink="/contact" routerLinkActive="active">Contacts</a>
The routerLink
attribute tells the router which route to activate when a user clicks on a given link. routerLinkActive
will then apply the specified CSS
class (or classes) to the element, giving us a visual clue of the current component.
Making everything come together in AppModule
Now all there is left to do is register our router and our components in AppModule
. Let’s register the routing component on the @NgModule
decorator like this:
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { HomeComponent } from './home.component';
import { AboutComponent } from './about.component';
import { ContactComponent } from './contact.component';
import { routing, appRoutingProviders } from './app.routing';
@NgModule ({
imports: [ BrowserModule, routing ],
declarations: [
AppComponent,
HomeComponent,
AboutComponent,
ContactComponent
],
bootstrap: [ AppComponent ]
})
export class AppModule {}
Testing our router app
That was quite a lot of code to set up some basic routing. The difficulty of setting up a base application is a common complaint among new Angular 2 users. But now that we’ve got past that point, let’s test our router.
If you haven’t installed all the necessary package dependencies it’s time to do so now. First, let’s bring all necessary npm
modules.
npm install
You may also have to install lite-server and typescript globally for the project to compile and run properly.
npm install -g lite-server
npm install -g typescript
npm install -g typings
Then, we can start our app with lite-server.
npm start
By default, lite-server should be running on port 3000. Head over to the URL below.
http://localhost:3000/
We can see that our navigation links are working properly. The URL reflects what section we’re in. The opposite is also true - when we enter the URL directly in the address bar the router parses it and loads the appropriate component.
In conclusion
We’ve successfully implemented routing in our Angular 2 application. We saw how the router can change the state of a Angular 2 application depending on the URI. The Angular 2 team put a lot of effort into the new router and went through multiple iterations across the release candidates.
This article serves as an introduction to Angular 2 routing capabilities. Router also supports more advanced features like route parameters, conditional access to routes (or guards
in Angular 2 lexicon), nested routes and asynchronous loading to load components on demand.
If you want to know how the router works in details, checkout this book, Angular 2 Router, written by Victor Savkin - the mind behind this router implementation.