THE AMAZING
ANGULAR 2
ROUTER
slides.com/gerardsans | @gerardsans
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2968660/comics-wallpaper-spiderman-marvel-images.png)
Google Developer Expert
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3077807/29418078933_e7b227e44d_o.jpg)
Master of Ceremonies
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3252480/Cv15n1dUsAEE4qo.jpg)
International Speaker
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3077778/14360083_874734925995922_360348503458709504_n.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2501216/jfokus-logo-300x216.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2501220/sharing-facebook.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2501222/logo2x.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2501240/Ghzm12MH__1_.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2501259/JAZOON3_w_techdays_darkred_200x48.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2551994/newlogo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3198520/logo_ffconf.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3252374/ngPoland.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3283531/logo_2.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3060481/home-logo_ori__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3248046/F9ACC5B15F071A4012AEA982630D0B2E.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3046178/AngularConnect.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3283595/download__9_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3283606/download__10_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3283657/2919-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3283784/Screen_Shot_2016-11-29_at_08.43.16.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3283808/download__11_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2159911/ng-nl-logo.png)
Angular 2 Trainer
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3077765/1-Kt1ieFXLC8wuwm-wt0sziQ__1_.jpeg)
Community Leader
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3077823/28311378825_756a12f091_o.jpg)
800
500
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3091031/Screen_Shot_2016-10-07_at_23.15.32.png)
Some days before
GDG DevFest London...
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2302826/106z0f.jpg)
Angular 2
Features
- Latest Web Standards
- Great Developer Experience
- Simple
- Lightning fast
- Works everywhere
TypeScript
ES6 (ES2015)
- Classes, modules, arrow functions
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/1791059/js-dialects-ven.png)
TypeScript
- Types, decorators, generics, interfaces
- Great editor support
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3091228/91401f763ecce827e2cd64f693460325.jpg)
TypeScript IDE Support
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2307366/Sublime_Text_Logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2420109/icon_WebStorm_copy.png)
Modern Tooling
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3061330/ScreenShot2016-09-29at19.34.24.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3061332/ScreenShot2016-09-29at19.34.24_2.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2756726/angular_high.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2302778/g1329366229914525740.jpg)
Application Design
Requirements
- Network resilience
- Ask before navigating
- Restrict access
- Lazy loading
Components Tree
/home
/users
/about
/users/34
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2300554/Screen_Shot_2016-03-02_at_22.51.09.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2301381/256-256-2744aa493427300de94d0be8a8f2edb9-clock.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2302876/Screen_Shot_2016-03-03_at_13.23.06.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2302880/Screen_Shot_2016-03-03_at_13.24.05.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2442581/Screen_Shot_2016-04-07_at_22.05.49.png)
Bootstrap
Bootstrapping
- Angular Application instantiation
- Root Module (AppModule)
-
Global Dependencies
- Router, Http, Services
- Global Values
- Vendor dependencies
index.html
<!DOCTYPE html>
<html>
<head>
<!-- Polyfill(s) for older browsers -->
<script src="https://unpkg.com/core-js/client/shim.min.js"></script>
<script src="https://unpkg.com/zone.js@0.6.17?main=browser"></script>
<script src="https://unpkg.com/reflect-metadata@0.1.3"></script>
<script src="https://unpkg.com/systemjs@0.19.27/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>System.import('app');</script>
</head>
<body>
<my-app>
Loading...
</my-app>
</body>
</html>
Bootstrap
// main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
Bootstrap
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
@NgModule({
imports: [ BrowserModule, UsersModule, appRouting ],
declarations: [ AppComponent, Home, ... ],
providers: [ LoginService, AuthCanActivate ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'my-app', // <my-app>Loading...</my-app>
template: `...`
})
export class App {
constructor() { }
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2442276/Screen_Shot_2016-04-07_at_21.08.32.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2442276/Screen_Shot_2016-04-07_at_21.08.32.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2442245/tumblr_n34fi1vhSW1suivyoo4_500__1_.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2302782/7ce7079d2bca29b856f7dc93ab00574a.jpg)
Services
/home
/users
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2302876/Screen_Shot_2016-03-03_at_13.23.06.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2442581/Screen_Shot_2016-04-07_at_22.05.49.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2442587/Screen_Shot_2016-04-07_at_22.06.45.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2442597/Screen_Shot_2016-04-07_at_22.08.09.png)
login.service.ts
import { Injectable } from '@angular/core';
@Injectable()
export class LoginService {
authorised = false;
authorise(value) {
this.authorised = value;
}
}
home.component.ts
// home.component.ts
import { Component } from '@angular/core';
import { LoginService } from './login.service';
@Component({
template: `
<h1>Home</h1>
<input #switch type="checkbox"
[checked]="loginService.authorised"
(click)="change(switch.checked)">
<div *ngIf="loginService.authorised">
<a>Today's best superheroe</a>
</div>`
})
export class Home {
constructor(private loginService:LoginService) { }
change(checked){
this.loginService.authorise(checked);
}
}
/users
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2302876/Screen_Shot_2016-03-03_at_13.23.06.png)
users.json
[{
"id": 34,
"username": "spiderman",
"roles": ["admin", "user"]
}, {
"id": 67,
"username": "batman",
"roles": ["user"]
}]
users.service.ts
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/retryWhen';
import 'rxjs/add/operator/delay';
@Injectable()
export class UsersService {
constructor(private http:Http) { }
get(){
return this.http.get('api/users.json')
.map(response => response.json())
.retryWhen(errors => errors.delay(2000));
}
}
users.component.ts
import { Component } from '@angular/core';
import { UsersService } from './users.service';
@Component({
selector: 'users',
template: `
<h1>Users</h1>
<table class="table">
<tr *ngFor="let user of users">
<td>{{user.username}}</td>
</tr>
</table>`
})
export class Users {
constructor(private service:UsersService){
service.get().subscribe(users => this.users = users);
}
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2444128/123411_4Jd1.png)
Router
Main Contributor
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2969727/h99AjyrP_400x400.jpg)
Main Features
- Url-based navigation
- Flexible routing
- Nested and auxiliary routes
- Navigation Guards
- Lazy loading
Setup Router
Dependencies
- Choose Location Strategy
- Setup routes
- Include providers and directives
-
RouterModule.forRoot(routes)
-
RouterModule.forChild(routes)
-
Location Strategies
- Hash Location
- Eg: #/home, #/users/34
- Path Location (default)
- Requires <base href=.../>
- Eg: /home, /users/34
Bootstrap
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRouting } from './app.routing';
@NgModule({
imports: [ BrowserModule, AppRouting, ... ],
declarations: [ ... ],
providers: [ ... ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
/home
/users
/about
/users/34
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2300554/Screen_Shot_2016-03-02_at_22.51.09.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2301381/256-256-2744aa493427300de94d0be8a8f2edb9-clock.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2302876/Screen_Shot_2016-03-03_at_13.23.06.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2302880/Screen_Shot_2016-03-03_at_13.24.05.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2442581/Screen_Shot_2016-04-07_at_22.05.49.png)
Defining Routes
// app.routing.ts
import { Routes, RouterModule } from '@angular/router';
import { Home } from './home.component';
import { About } from './about.component';
import { NotFound } from './notfound.component';
const appRoutes: Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: Home },
{ path: '**', component: NotFound }, //always last
];
export const AppRouting = RouterModule.forRoot(appRoutes, { useHash: true });
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2442581/Screen_Shot_2016-04-07_at_22.05.49.png)
router-outlet
// app.component.ts
@Component({
selector: 'my-app',
template: `
<nav></nav>
<main>
<router-outlet></router-outlet>
</main>`
})
export class App { }
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2444164/9993d1327987625-60s-spiderman-meme-.jpg)
Child Routes
users
#/users
Users
users/:id
#/users/34
User
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2302876/Screen_Shot_2016-03-03_at_13.23.06.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2302880/Screen_Shot_2016-03-03_at_13.24.05.png)
Child Routes
// users.routing.ts
import { Routes, RouterModule } from '@angular/router';
import { Users } from './users.component';
import { User } from './user.component';
const usersRoutes: Routes = [
{ path: 'users', component: Users },
{ path: 'users/:id', component: User }
];
export const UsersRouting = RouterModule.forChild(usersRoutes);
Navigation
- Using regular links with hash
-
#/home
-
- Using routerLink directive
-
['home']
-
['users', 34] /users/:id
-
- Programatically
-
router.navigate(['users', 34])
-
router.navigateByUrl('/users/34')
-
routerLink
import { Component } from '@angular/core';
@Component({
selector: 'users',
template: `
<h1>Users</h1>
<tr *ngFor="let user of users">
<td>
<a [routerLink]="['/users', user.id]">{{user.username}}</a>
</td>
</tr>
`
})
export class Users { }
Some examples
<!-- Top Routes -->
<a href="#/home">Home</a>
<a [routerLink]="['home']">Home</a>
<!-- Nested Routes -->
<a href="#/users">Users</a>
<a [routerLink]="['users']">Users</a>
<a href="#/users/34">Spiderman</a>
<a [routerLink]="['users', 34]">Spiderman</a>
Some examples
<!-- hash fragments -->
<a href="#/users/34#section">Spiderman</a>
<a [routerLink]="['users', 34]" fragment="section">Spiderman</a>
<!-- params and queryParams -->
<a href="#/users/34;flag=true?q=1">Spiderman</a>
<a [routerLink]="['users', 34, {flag: true}]" [queryParams]="{q: 1}">Spiderman</a>
Current Url
// { path: 'users/:id', component: User } Url: #/users/34
import { Router } from '@angular/router';
@Component({
selector: 'user-details'
})
export class User implements OnInit {
constructor(private router: Router){ }
ngOnInit() {
console.log(this.router.url); // /users/34
}
}
Accessing hash fragment
// { path: 'users/:id', component: User } Url: #/users/34#section
import { Router } from '@angular/router';
@Component({
selector: 'user-details'
})
export class User implements OnInit {
constructor(private router: Router){
router.routerState.root.fragment.subscribe(f => {
let fragment = f; // section
});
}
ngOnInit() {
let fragment = this.router.routerState.snapshot.root.fragment; // section
}
}
Accessing queryParams
// { path: 'users/:id', component: User } Url: #/users/34?q=1
import { Router } from '@angular/router';
@Component({
selector: 'user-details'
})
export class User implements OnInit {
constructor(private router: Router){
router.routerState.root.queryParams.subscribe(params => {
let q = params.q; // 1
});
}
ngOnInit() {
let q = this.router.routerState.snapshot.root.queryParams.q; // 1
}
}
Accessing Data
// { path: 'users/:id', component: User, data: { key: 1 } }
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'user-details'
})
export class User implements OnInit {
constructor(private route: ActivatedRoute){
route.data.subscribe(data => {
let key = data.key; // 1
});
}
ngOnInit() {
let key = this.route.snapshot.data.key; // 1
}
}
Accessing Parameters
// { path: 'users/:id', component: User } Url: #/users/34;flag=true
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'user-details'
})
export class User implements OnInit {
constructor(private route: ActivatedRoute){
route.params.subscribe(params => {
let id = params.id; // 34
});
}
ngOnInit() {
let id = this.route.snapshot.params.id; // 34
let flag = this.route.snapshot.params.flag; // true
}
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2444142/27530942.jpg)
Navigation Guards
Ask before navigating
// users.routing.ts
import { Routes, RouterModule } from '@angular/router';
import { UserCanDeactivate } from './user.canDeactivate';
const usersRoutes: Routes = [
{ path: 'users', component: Users },
{ path: 'users/:id', component: User,
canDeactivate: [UserCanDeactivate]
}
];
export const UsersRouting = RouterModule.forChild(usersRoutes);
Ask before navigating
// user.canDeactivate.ts
import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
Injectable()
export class UserCanDeactivate implements CanDeactivate {
canDeactivate() {
return window.confirm('Do you want to continue?');
}
}
Restrict Access
// users.routing.ts
import { Routes, RouterModule } from '@angular/router';
import { AuthCanActivate } from './auth.canActivate';
const usersRoutes: Routes = [
{ path: 'users', component: Users },
{ path: 'users/:id', component: User,
canDeactivate: [UserCanDeactivate],
canActivate: [AuthCanActivate]
}
];
export const usersRouting = RouterModule.forChild(usersRoutes);
Restrict Access
// auth.canActivate.ts
import { Injectable } from '@angular/core';
import { Router, CanActivate } from '@angular/router';
import { LoginService } from './login.service';
export class AuthCanActivate implements CanActivate {
constructor(private loginService: LoginService, private router: Router) {}
canActivate() {
if (!this.loginService.authorised) {
this.router.navigate(['/home']);
return false;
}
return true;
}
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3091224/tumblr_o8mpv05Dzk1s2wio8o1_500.gif)
Lazy Loading
Lazy Loading
// app.routing.ts
const aboutRoutes: Routes = [
{ path: 'about', loadChildren: './app/about.module.ts' },
];
export const AboutRouting = RouterModule.forChild(aboutRoutes);
//about.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AboutRouting } from './about.routing';
import { About } from './about.component';
@NgModule({
imports: [ CommonModule, AboutRouting ],
declarations: [ About ]
})
export default class AboutModule { }
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/2302778/g1329366229914525740.jpg)
Advanced Features
Auxiliary routes
// app.routing.ts
const routes: Routes = [
{path: 'home', component: HelloCmp},
{path: 'banner', component: Banner, outlet: 'bottom'}
];
// app.component.ts
@Component({
selector: 'my-app',
template: `
<router-outlet></router-outlet>
<router-outlet name="bottom"></router-outlet>`,
})
export class App { }
// templates/code
<a href="#/home(bottom:banner)">Show Banner!</a>
<a [routerLink]="['/home(bottom:banner)']">Show Banner!</a>
router.navigate('/home(bottom:banner)');
Resolve
// about.routing.ts
import { Routes, RouterModule } from '@angular/router';
import { About } from './about.component';
import { AboutResolver } from './about.resolver';
const aboutRoutes: Routes = [
{ path: '', component: About,
resolve: { magicNumber: AboutResolver }
}
];
export const AboutRouting = RouterModule.forChild(aboutRoutes);
Resolve
// about.resolver.ts
import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
@Injectable()
export class AboutResolver implements Resolve {
resolve() {
console.log('Waiting to resolve...');
return new Promise(resolve => setTimeout(() => resolve(4545), 2000));
}
}
Resolve
// about.component.ts
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'about',
template: `
<h1>About</h1>
<p>Magic Number: {{magicNumber}}</p>`
})
export class About {
magicNumber: number;
constructor(private route: ActivatedRoute){
this.magicNumber = route.snapshot.data.magicNumber;
}
}
Demo
Features
- Nested routes with parameters
- Asking before leaving a route
- Restricting route access
- Cool SVG loading spinner
- Lazy Loading and resolve
Thanks!
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3091195/spiderman_byebye.png)
The Amazing Angular 2 Router
By Gerard Sans
The Amazing Angular 2 Router
Angular 2 comes with a new router. We will review its main features and show how we can use navigation guards, secure sections, use nested routes and do lazy load while quickly building a prototype for a very special client!
- 4,804