18. Routing & Navigation
AppComponent (focus Technology Feature):
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h1>{{ title }}</h1>
<div class="left">
<h1>Angular Router</h1>
<nav>
<a routerLink="/crisis-center" routerLinkActive="active">Crisis Center</a>
<a routerLink="/technologies" routerLinkActive="active">Technologies</a>
</nav>
<router-outlet></router-outlet>
</div>
<div class="right">
<img [src]="imageUrl" class="img-round" alt="princess image">
</div>
`,
styles: [`h1 { font-weight: bold; }`]
})
export class AppComponent {
title = 'Tour of Technologies';
imageUrl = '../assets/polymer1.jpg-large';
}
AppRoutingModule (focus Technology Feature):
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CrisisListComponent } from './crisis-list/crisis-list.component';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
const appRoutes: Routes = [
{ path: 'crisis-center', component: CrisisListComponent },
{ path: '', redirectTo: '/technologies', pathMatch: 'full' },
{ path: '**', component: PageNotFoundComponent }
];
@NgModule({
imports: [
RouterModule.forRoot(appRoutes, { enableTracing: true })
],
exports: [ RouterModule ]
})
export class AppRoutingModule { }
TechnologyListComponent(focus Technology Feature):
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { Technology, TechnologyService } from './technology.service';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/switchMap';
@Component({
template: `
<h2>TECHNOLOGIES</h2>
<p>Build great apps here</p>
<ul class="items">
<li *ngFor="let technology of technologies$ | async"
[class.selected]="technology.id === selectedId">
<a [routerLink]="['/technology', technology.id]">
<span class="badge">{{ technology.id }}</span>{{ technology.name }}
</a>
</li>
</ul>
<button routerLink="/sidekicks">Go to sidekicks</button>
`
})
export class TechnologyListComponent implements OnInit {
technologies$: Observable<Technology[]>;
selectedId: number;
constructor(private technologyService: TechnologyService,
private route: ActivatedRoute) { }
ngOnInit() {
this.technologies$ = this.route.paramMap.switchMap((params: ParamMap) => {
this.selectedId = +params.get('id');
return this.technologyService.getTechnologies();
});
}
}
TechnologyDetailComponent(focus Technology Feature):
import { Component, OnInit, HostBinding } from '@angular/core';
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
import { slideInDownAnimation } from '../animations';
import { Technology, TechnologyService } from './technology.service';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/switchMap';
@Component({
template: `
<h2>TECHNOLOGIES</h2>
<div *ngIf="technology$ | async as technology">
<h3>"{{ technology.name }}"</h3>
<div>
<label>Id: </label>
</div>
<div>
<label>Name: </label>
<input [(ngModel)]="technology.name" placeholder="name">
</div>
<p>
<button (click)="gotoTechnologies(technology)">Back</button>
</p>
</div>
`,
animations: [ slideInDownAnimation ]
})
export class TechnologyDetailComponent implements OnInit {
@HostBinding('@routeAnimation') routeAnimation = true;
@HostBinding('style.display') display = 'block';
@HostBinding('style.position') position = 'absolute';
technology$: Observable<Technology>;
constructor(private route: ActivatedRoute,
private router: Router,
private technologyService: TechnologyService) { }
ngOnInit() {
this.technology$ = this.route.paramMap
.switchMap((params: ParamMap) =>
this.technologyService.getTechnology(params.get('id')));
}
gotoTechnologies(technology: Technology) {
const technologyId = technology ? technology.id : null;
this.router.navigate(['/technologies', { id: technologyId, foo: 'bar'}]);
}
}
TechnologyService (focus Technology Feature):
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map';
export class Technology {
constructor(public id: number, public name: string) { }
}
const TECHNOLOGIES = [
new Technology(11, 'Angular 4'),
new Technology(12, 'Angular CLI'),
new Technology(13, 'Angular Material'),
new Technology(14, 'Angular 4 Dart'),
new Technology(15, 'Ionic 3'),
new Technology(16, 'Polymer 3.0')
];
@Injectable()
export class TechnologyService {
getTechnologies() { return Observable.of(TECHNOLOGIES); }
getTechnology(id: number | string) {
return this.getTechnologies()
.map(technologies => technologies.find(technology => technology.id === +id));
}
}
TechnologiesModule (focus Technology Feature):
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { TechnologyRoutingModule } from './technologies-routing.module';
import { TechnologyListComponent } from './technology-list.component';
import { TechnologyDetailComponent } from './technology-detail.component';
import { TechnologyService } from './technology.service';
@NgModule({
imports: [ CommonModule, FormsModule, TechnologyRoutingModule ],
declarations: [ TechnologyListComponent, TechnologyDetailComponent ],
providers: [ TechnologyService ]
})
export class TechnologiesModule { }
TechnologiesRoutingModule (focus Technology Feature):
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TechnologyListComponent } from './technology-list.component';
import { TechnologyDetailComponent } from './technology-detail.component';
const technologiesRoutes: Routes = [
{ path: 'technologies', redirectTo: '/supertechnologies' },
{ path: 'technology/:id', redirectTo: '/supertechnology/:id' },
{ path: 'supertechnologies', component: TechnologyListComponent },
{ path: 'supertechnology/:id', component: TechnologyDetailComponent }
];
@NgModule({
imports: [ RouterModule.forChild(technologiesRoutes) ],
exports: [ RouterModule ]
})
export class TechnologyRoutingModule { }
Animations (focus Technology Feature):
import { animate, AnimationEntryMetadata, state, style, transition, trigger } from '@angular/core';
export const slideInDownAnimation: AnimationEntryMetadata =
trigger('routeAnimation', [
state('*',
style({
opacity: 1,
transform: 'translateX(0)'
})
),
transition(':enter', [
style({
opacity: 0,
transform: 'translateX(-100%)'
}),
animate('0.3s ease-in')
]),
transition(':leave', [
animate('0.7s ease-out', style({
opacity: 0,
transform: 'translateY(100%)'
}))
])
]);
Let's have a look at this beauty in Chromium:
AppComponent (focus Crisis Feature):
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h1>{{ title }}</h1>
<div class="left">
<h1>Angular Router</h1>
<nav>
<a routerLink="/crisis-center" routerLinkActive="active">Crisis Center</a>
<a routerLink="/technologies" routerLinkActive="active">Technologies</a>
</nav>
<router-outlet></router-outlet>
</div>
<div class="right">
<img [src]="imageUrl" class="img-round" alt="princess image">
</div>
`,
styles: [`h1 { font-weight: bold; }`]
})
export class AppComponent {
title = 'Tour of Technologies';
imageUrl = '../assets/polymer5.jpg';
}
AppRoutingModule(focus Crisis Feature):
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
import { CanDeactivateGuard } from './can-deactivate-guard.service';
import { SelectivePreloadingStrategy } from './selective-preloading.strategy';
const appRoutes: Routes = [
{
path: 'crisis-center',
loadChildren: 'app/crisis-center/crises-center.module#CrisisCenterModule',
data: { preload: true }
},
{ path: '', redirectTo: '/supertechnologies', pathMatch: 'full' },
{ path: '**', component: PageNotFoundComponent }
];
@NgModule({
imports: [
RouterModule.forRoot(appRoutes,
{ enableTracing: false, preloadingStrategy: SelectivePreloadingStrategy })
],
exports: [ RouterModule ],
providers: [ CanDeactivateGuard, SelectivePreloadingStrategy ]
})
export class AppRoutingModule { }
CrisisCenterModule(focus Crisis Feature):
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { CrisesCenterRoutingModule } from './crises-center-routing.module';
import { CrisisCenterHomeComponent } from './crisis-center-home.component';
import { CrisisListComponent } from './crisis-list.component';
import { CrisisDetailComponent } from './crisis-detail.component';
import { CrisisCenterComponent } from './crisis-center.component';
import { CrisisService } from './crisis.service';
@NgModule({
imports: [ CommonModule, FormsModule, CrisesCenterRoutingModule ],
declarations: [ CrisisListComponent, CrisisDetailComponent,
CrisisCenterComponent, CrisisCenterHomeComponent ],
providers: [ CrisisService ]
})
export class CrisisCenterModule { }
CrisesCenterRoutingModule(focus Crisis Feature):
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CrisisCenterComponent } from './crisis-center.component';
import { CrisisListComponent } from './crisis-list.component';
import { CrisisDetailComponent } from './crisis-detail.component';
import { CrisisCenterHomeComponent } from './crisis-center-home.component';
import { CanDeactivateGuard } from '../can-deactivate-guard.service';
import { CrisisDetailResolver } from './crisis-detail-resolver.service';
const crisisCenterRoutes: Routes = [
{
path: '',
component: CrisisCenterComponent,
children: [
{
path: '',
component: CrisisListComponent,
children: [
{
path: ':id',
component: CrisisDetailComponent,
canDeactivate: [ CanDeactivateGuard ],
resolve: {
crisis: CrisisDetailResolver
}
},
{
path: '',
component: CrisisCenterHomeComponent
}
]
}
]
}
];
@NgModule({
imports: [ RouterModule.forChild(crisisCenterRoutes) ],
exports: [ RouterModule ],
providers: [ CrisisDetailResolver ]
})
export class CrisesCenterRoutingModule { }
CrisisCenterHomeComponent(focus Crisis Feature):
import { Component } from '@angular/core';
@Component({
template: `
<p>Welcome to the Crisis Center</p>
`
})
export class CrisisCenterHomeComponent { }
CrisisCenterComponent(focus Crisis Feature):
import { Component } from '@angular/core';
@Component({
template: `
<h2>CRISIS CENTER</h2>
<router-outlet></router-outlet>
`
})
export class CrisisCenterComponent { }
CrisisDetailResolver(focus Crisis Feature):
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Router, Resolve, RouterStateSnapshot, ActivatedRouteSnapshot } from '@angular/router';
import { Crisis, CrisisService } from './crisis.service';
@Injectable()
export class CrisisDetailResolver implements Resolve<Crisis> {
constructor(private crisisService: CrisisService, private router: Router) { }
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Crisis> {
const id = route.paramMap.get('id');
return this.crisisService.getCrisis(id).take(1).map(crisis => {
if (crisis) {
return crisis;
} else {
this.router.navigate(['/crisis-center']);
return null;
}
});
}
}
CrisisDetailComponent(focus Crisis Feature):
import { Component, OnInit, HostBinding } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { slideInDownAnimation } from '../animations';
import { Crisis } from './crisis.service';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/switchMap';
@Component({
template: `
<div *ngIf="crisis">
<h3>"{{ editName }}"</h3>
<div>
<label>Id: </label>{{ crisis.id }}
</div>
<div>
<label>Name: </label>
<input [(ngModel)]="editName" placeholder="name">
</div>
<p>
<button (click)="save()">Save</button>
<button (click)="cancel()">Cancel</button>
</p>
</div>
`,
styles: ['input { width: 20em; }'],
animations: [ slideInDownAnimation ]
})
export class CrisisDetailComponent implements OnInit {
@HostBinding('@routeAnimation') routeAnimation = true;
@HostBinding('style.display') display = 'block';
@HostBinding('style.position') position = 'absolute';
crisis: Crisis;
editName: string;
constructor(private route: ActivatedRoute,
private router: Router) { }
ngOnInit() {
this.route.data
.subscribe((data: { crisis: Crisis}) => {
this.editName = data.crisis.name;
this.crisis = data.crisis;
});
}
cancel() {
this.gotoCrises();
}
save() {
this.crisis.name = this.editName;
this.gotoCrises();
}
gotoCrises() {
const crisisId = this.crisis ? this.crisis.id : null;
this.router.navigate(['../', { id: crisisId, foo: 'bar'}], { relativeTo: this.route });
}
}
CrisisListComponent(focus Crisis Feature):
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { Crisis, CrisisService } from './crisis.service';
import { Observable } from 'rxjs/Observable';
@Component({
template: `
<ul class="items">
<li *ngFor="let crisis of crises$ | async"
[class.selected]="crisis.id === selectedId">
<a [routerLink]="[crisis.id]">
<span class="badge">{{ crisis.id }}</span>{{ crisis.name }}
</a>
</li>
</ul>
<router-outlet></router-outlet>
`
})
export class CrisisListComponent implements OnInit {
crises$: Observable<Crisis[]>;
selectedId: number;
constructor(private crisisService: CrisisService, private route: ActivatedRoute) { }
ngOnInit() {
this.crises$ = this.route.paramMap.switchMap((params: ParamMap) => {
this.selectedId = +params.get('id');
return this.crisisService.getCrises();
});
}
}
CrisisService(focus Crisis Feature):
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
export class Crisis {
constructor(public id: number, public name: string) { }
}
const CRISES = [
new Crisis(1, 'Angular 4 Love Affair GitBook isn\'t completed yet'),
new Crisis(2, 'New Angular 4 App not already deployed'),
new Crisis(3, 'Angular 4 Tutorial not published yesterday'),
new Crisis(4, 'My Polymer Princess is in danger, Protect her'),
new Crisis(5, 'Code Angular Documentation one more time')
];
@Injectable()
export class CrisisService {
getCrises() { return Observable.of(CRISES); }
getCrisis(id: number | string) {
return this.getCrises()
.map(crises => crises.find(crisis => crisis.id === +id));
}
}
Let's view the crises in our Chromium Browser:
AppComponent(focus Admin Feature):
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h1>{{ title }}</h1>
<div class="left">
<nav>
<a routerLink="/crisis-center" routerLinkActive="active">Crisis Center</a>
<a routerLink="/technologies" routerLinkActive="active">Technologies</a>
<a routerLink="/admin" routerLinkActive="active">Admin</a>
<a routerLink="/login" routerLinkActive="active">Login</a>
<a [routerLink]="[{ outlets: { popup: ['compose'] }}]">Contact</a>
</nav>
<router-outlet></router-outlet>
<router-outlet name="popup"></router-outlet>
</div>
<div class="right">
<img [src]="imageUrl" class="img-round" alt="princess image">
</div>
`,
styles: [`h1 { font-weight: bold; }`]
})
export class AppComponent {
title = 'Tour of Technologies';
imageUrl = '../assets/polymer5.jpg';
}
AppRoutingModule(focus Admin Feature):
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AuthGuard } from '../auth-guard.service';
import { AdminComponent } from './admin.component';
import { ManageCrisesComponent } from './manage-crises.component';
import { ManageTechnologiesComponent } from './manage-technologies.component';
import { AdminDashboardComponent } from './admin-dashboard.component';
const adminRoutes: Routes = [
{
path: 'admin',
component: AdminComponent,
canActivate: [ AuthGuard ],
children: [
{
path: '',
canActivateChild: [ AuthGuard ],
children: [
{ path: 'crises', component: ManageCrisesComponent },
{ path: 'technologies', component: ManageTechnologiesComponent },
{ path: '', component: AdminDashboardComponent }
]
}
]
}
];
@NgModule({
imports: [ RouterModule.forChild(adminRoutes) ],
exports: [ RouterModule ]
})
export class AdminRoutingModule { }
AdminComponent(focus Admin Feature):
import { Component } from '@angular/core';
@Component({
template: `
<h3>ADMIN</h3>
<nav>
<a routerLink="./" routerLinkActive="active"
[routerLinkActiveOptions]="{ exact: true }">Dashboard</a>
<a routerLink="./crises" routerLinkActive="active">Manage Crises</a>
<a routerLink="./technologies" routerLinkActive="active">Manage Technologies</a>
</nav>
<router-outlet></router-outlet>
`
})
export class AdminComponent { }
AdminDashboardComponent(focus Admin Feature):
import { Component } from '@angular/core';
@Component({
template: `
<p>Dashboard</p>
`
})
export class AdminDashboardComponent {
}
ManageCrisesComponent(focus Admin Feature):
import { Component } from '@angular/core';
@Component({
template: `
<p>Manage your crises here</p>
`
})
export class ManageCrisesComponent { }
ManageTechnologiesComponent(focus Admin Feature):
import { Component } from '@angular/core';
@Component({
template: `
<p>Manage your technologies here</p>
`
})
export class ManageTechnologiesComponent { }
AdminModule(focus Admin Feature):
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AdminComponent } from './admin.component';
import { AdminDashboardComponent } from './admin-dashboard.component';
import { ManageCrisesComponent } from './manage-crises.component';
import { ManageTechnologiesComponent } from './manage-technologies.component';
import { AdminRoutingModule } from './admin-routing.module';
@NgModule({
imports: [ CommonModule, AdminRoutingModule ],
declarations: [ AdminComponent, AdminDashboardComponent,
ManageCrisesComponent, ManageTechnologiesComponent]
})
export class AdminModule { }
LoginComponent(focus Admin Feature):
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from './auth.service';
@Component({
template: `
<h2>LOGIN</h2>
<p>{{ message }}</p>
<p>
<button (click)="login()" *ngIf="!authService.isLoggedIn">Login</button>
<button (click)="logout()" *ngIf="authService.isLoggedIn">Logout</button>
</p>
`
})
export class LoginComponent {
message: string;
constructor(public authService: AuthService, public router: Router) {
this.setMessage();
}
private setMessage() {
this.message = 'Logged ' + (this.authService.isLoggedIn ? 'in' : 'out');
}
login() {
this.message = 'Trying to log in ...';
this.authService.login().subscribe(() => {
this.setMessage();
if (this.authService.isLoggedIn) {
// get redirect url from our auth service
// if no redirect has been set, use the default
const redirect = this.authService.redirectUrl ?
this.authService.redirectUrl : '/crisis-center/admin';
// redirect user
this.router.navigate([redirect]);
}
});
}
logout() {
this.authService.logout();
this.setMessage();
}
}
LoginRoutingModule(focus Admin Feature):
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './auth-guard.service';
import { AuthService } from './auth.service';
import { LoginComponent } from './login.component';
const loginRoutes: Routes = [
{ path: 'login', component: LoginComponent }
];
@NgModule({
imports: [ RouterModule.forChild(loginRoutes) ],
exports: [ RouterModule ],
providers: [ AuthGuard, AuthService ]
})
export class LoginRoutingModule { }
AuthService(focus Admin Feature):
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/delay';
@Injectable()
export class AuthService {
isLoggedIn = false;
// store URL so we can redirect after logging in
redirectUrl: string;
login(): Observable<boolean> {
return Observable.of(true).delay(1500).do(val => this.isLoggedIn = true);
}
logout(): void {
this.isLoggedIn = false;
}
}
AuthGuard(focus Admin Feature):
import { Injectable } from '@angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot,
RouterStateSnapshot, CanActivateChild } from '@angular/router';
import { AuthService } from './auth.service';
@Injectable()
export class AuthGuard implements CanActivate, CanActivateChild {
constructor(private authService: AuthService, private router: Router) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
const url: string = state.url;
return this.checkLogin(url);
}
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
return this.canActivate(route, state);
}
checkLogin(url: string): boolean {
if (this.authService.isLoggedIn) { return true; }
// store attempted url for redirecting
this.authService.redirectUrl = url;
// navigate to login page with extras
this.router.navigate(['/login']);
return false;
}
}
Let's have a look at the results in Chromium:
Route Guards:
import { Injectable } from '@angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot,
RouterStateSnapshot, CanActivateChild, NavigationExtras } from '@angular/router';
import { AuthService } from './auth.service';
@Injectable()
export class AuthGuard implements CanActivate, CanActivateChild {
constructor(private authService: AuthService, private router: Router) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
const url: string = state.url;
return this.checkLogin(url);
}
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
return this.canActivate(route, state);
}
checkLogin(url: string): boolean {
if (this.authService.isLoggedIn) { return true; }
// store attempted url for redirecting
this.authService.redirectUrl = url;
const sessionId = 1234567890;
const navigationExtras: NavigationExtras = {
queryParams: { 'session_id': sessionId},
fragment: 'anchor'
};
// navigate to login page with extras
this.router.navigate(['/login'], navigationExtras);
return false;
}
}
Route Guards:
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
@Component({
template: `
<p>Dashboard</p>
<p>Session ID: {{ sessionId | async }}</p>
<a id="anchor"></a>
<p>Token: {{ token | async }}</p>
`
})
export class AdminDashboardComponent implements OnInit {
sessionId: Observable<string>;
token: Observable<string>;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
// capture sessionid if available
this.sessionId = this.route.queryParamMap.map(params => params.get('session_id') || 'None');
// capture fragment if available
this.token = this.route.fragment.map(fragment => fragment || 'None');
}
}
Asynchronous Routing (Lazy Loading with loadChildren):
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ComposeMessageComponent } from './compose.message.component';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
import { CanDeactivateGuard } from './can-deactivate-guard.service';
import { AuthGuard } from './auth-guard.service';
import { SelectivePreloadingStrategy } from './selective-preloading.strategy';
const appRoutes: Routes = [
{
path: 'compose',
component: ComposeMessageComponent,
outlet: 'popup'
},
{
path: 'admin',
loadChildren: 'app/admin/admin.module#AdminModule',
canLoad: [ AuthGuard ]
},
{
path: 'crisis-center',
loadChildren: 'app/crisis-center/crises-center.module#CrisisCenterModule',
data: { preload: true }
},
{ path: '', redirectTo: '/supertechnologies', pathMatch: 'full' },
{ path: '**', component: PageNotFoundComponent }
];
@NgModule({
imports: [
RouterModule.forRoot(appRoutes,
{ enableTracing: false, preloadingStrategy: SelectivePreloadingStrategy })
],
exports: [ RouterModule ],
providers: [ CanDeactivateGuard, SelectivePreloadingStrategy ]
})
export class AppRoutingModule { }