THE AMAZING
by Gerard Sans (@gerardsans)

ROUTER v3
Google Developer Expert

Master of Ceremonies

International Speaker



















Angular 2 Trainer

Community Leader

800
500

1x 35% discount ng-book 2 (£13)

@angular_zone #jslnd
Some days before the meetup...

Angular 2
Features
- Latest Web Standards
- Simple
- Lightning fast
- Works everywhere
ES5 / ES6 / TypeScript
ES6 (ES2015)
- Classes, modules, arrow functions
TypeScript
- Types, annotations
- Better editor support

TypeScript IDE Support




Application Design
Component Tree

source: blog
/home
/users
/about
/users/34





Bootstrap
Bootstrapping
- Angular Application instantiation
- Root component (App)
-
Global Dependencies
- Router, Http, Services
- Global Values
- Vendor dependencies
index.html
<!DOCTYPE html>
<html>
<head>
<!-- Polyfill(s) for older browsers -->
<script src=".../shim.min.js"></script>
<script src=".../zone.js@0.6.12?main=browser"></script>
<script src=".../reflect-metadata@0.1.3"></script>
<script src=".../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 { bootstrap } from '@angular/platform-browser-dynamic';
import { App } from './app.component';
import { LoginService } from './login.service';
...
bootstrap(App, [
// Global dependencies
LoginService, // Singleton
])
.catch(err => console.error(err));
app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'my-app', // <my-app>Loading...</my-app>
template: `...`
})
export class App {
constructor() { }
}




Services
/home
/users




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

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 | async">
<td>{{user.username}}</td>
</tr>
</table>`
})
export class Users {
constructor(private service:UsersService){
this.users = service.get();
}
}

Component Router
Main Contributor

Main Features
- Based on components
- Flexible routing
- Nested views, auxiliary routes
- Navigation Gards
- Lazy loading (rc.5)
Setup Router
Dependencies
- Choose Location Strategy
- Setup routes
- Include providers
- provideRouter(routes)
- Include directives
- ROUTER_DIRECTIVES
Location Strategies
- HashLocationStrategy
- Eg: #/home, #/users/34
- PathLocationStrategy (default)
- Requires <base href=.../>
- Eg: /home, /users/34
Bootstrap
import { provideRouter } from '@angular/router';
import { HashLocationStrategy, LocationStrategy } from '@angular/common';
import { routes } from './app.routes';
bootstrap(App, [
provideRouter(routes),
{ provide: LocationStrategy, useClass: HashLocationStrategy },
])
/home
/users
/about
/users/34





Defining Routes
// app.routes.ts
import { Home } from './home.component';
import { About } from './about.component';
import { NotFound } from './notfound.component';
export const routes: RouterConfig = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: Home },
{ path: 'about', component: About },
{ path: '**', component: NotFound }, //always last
];

router-outlet
// app.component.ts
import { ROUTER_DIRECTIVES } from '@angular/router';
@Component({
selector: 'my-app',
template: `
<nav></nav>
<main>
<router-outlet></router-outlet>
</main>`,
directives: [ROUTER_DIRECTIVES]
})
export class App { }

Child Routes
users
#/users
Users
users/:id
#/users/34
User


Child Routes
// users.routes.ts
import { RouterConfig } from '@angular/router';
import { Users } from './users.component';
import { User } from './user.component';
export const usersRoutes: RouterConfig = [
{ path: 'users', component: Users },
{ path: 'users/:id', component: User }
];
Navigation
- Using regular links with hash
-
#/home
-
- Using routerLink directive
-
['home']
-
['users', 34] //users/:id
-
- Programatically
-
router.navigate(['users', 34]);
-
routerLink
import { Component } from '@angular/core';
import { ROUTER_DIRECTIVES } from '@angular/router';
@Component({
selector: 'users',
template: `
<h1>Users</h1>
<tr *ngFor="let user of users">
<td>
<a [routerLink]="['/users', user.id]">{{user.username}}</a>
</td>
</tr>
`,
directives: [ROUTER_DIRECTIVES]
})
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>
<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 }
import { ActivatedRoute, Router } from '@angular/router';
@Component({
selector: 'user-details'
})
export class User implements OnInit {
constructor(private route: ActivatedRoute, private router: Router){
this.route.url.subscribe(s =>
console.log(router.createUrlTree(s).toString()) // /users/34
);
}
ngOnInit() {
console.log(this.router.url); // /users/34
}
}
Accessing Parameters
// { path: 'users/:id', component: User }
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;
});
}
ngOnInit() {
let id = this.route.snapshot.params.id;
}
}
Accessing queryParams
// #/users/34;flag=true?q=1
import { Router } from '@angular/router';
@Component({
selector: 'user-details'
})
export class User implements OnInit {
constructor(private router: Router){
router.routerState.queryParams.subscribe(params => {
let q = params.q;
});
}
ngOnInit() {
let q = this.router.routerState.snapshot.queryParams.q;
}
}
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(v => console.log(v));
}
ngOnInit() {
let data = this.route.snapshot.data.key;
}
}

Navigation Gards
Ask before navigating
// users.routes.ts
import { RouterConfig } from '@angular/router';
import { UserCanDeactivate } from './user.canDeactivate';
export const usersRoutes: RouterConfig = [
{ path: 'users', component: Users },
{
path: 'users/:id', component: User,
canDeactivate: [UserCanDeactivate]
}
];
Ask before navigating
// user.canDeactivate.ts
import { CanDeactivate } from '@angular/router';
import { User } from './user.component';
Injectable()
export class UserCanDeactivate implements CanDeactivate<User> {
canDeactivate() {
return window.confirm('Do you want to continue?');
}
}
Restrict Access
// users.routes.ts
import { RouterConfig } from '@angular/router';
import { AuthCanActivate } from './auth.canActivate';
export const usersRoutes: RouterConfig = [
{ path: 'users', component: Users },
{
path: 'users/:id', component: User,
canDeactivate: [UserCanDeactivate],
canActivate: [AuthCanActivate]
}
];
Restrict Access
// auth.canActivate.ts
import { CanActivate, Router } 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;
}
}
Resolve
// app.routes.ts
import { AboutResolver } from './about.resolver';
export const routes: RouterConfig = [
{ path: 'home', component: Home },
{
path: 'about', component: About,
resolve: { magicNumber: AboutResolver }
},
];
Resolve
// about.resolver.ts
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class AboutResolver implements Resolve {
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<any> {
console.log('Waiting to resolve...');
let p = new Promise(resolve => setTimeout(() => resolve(4545), 2000));
return Observable.from(p);
}
}
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;
}
}

Advanced Features
Auxiliary routes
// app.routes.ts
export const routes: RouterConfig = [
{path: 'home', component: HelloCmp},
{path: 'banner', component: BottomCmp, 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)');
Lazy Loading (v2)
@RouteConfig([
{ path: '/about', loader: AboutLazyLoader , name: 'AboutLazyLoad' }
])
export class App { }
//aboutLazyLoader.ts
export function AboutLazyLoader(){
return System.import('./src/aboutLazyLoad.ts')
.then(module => module.AboutLazyLoad);
}
//aboutLazyLoad.ts
@Component({
selector: 'about',
template: `<h1>About</h1>`
})
export class AboutLazyLoad { }

Demo
Features
- Nested routes with parameters
- Asking before leaving a route
- Restricting route access
- Cool SVG loading spinner
AngularConnect - Sept 27-28th


Thanks!

The Amazing Angular 2 Router v3
By Gerard Sans
The Amazing Angular 2 Router v3
Angular 2 comes with a new router that replaces previous ngRoute. We will review its main features and show how we can secure sections, use nested routes and do lazy load while building a project for spiderman!
- 6,490