N Things You Don’t Know
About the Router
N > 0
Deborah Kurata
Developer | Author | Consultant | MVP | GDE
@deborahkurata
How Routing Works
<a [routerLink]="['/movies']">Movie List</a>
{ path: 'movies', component: MovieListComponent }
[Link]
<router-outlet></router-outlet> import { Component } from '@angular/core';
@Component({
templateUrl: '[Link]'
})
export class MovieListComponent { }
To Menu
or Not To Menu
[Link]
<div class="container">
<nav class="navbar navbar-default">
<div class="container-fluid">
<a class="navbar-brand">{{pageTitle}}</a>
<ul class="nav navbar-nav">
<li><a [routerLink]="['/welcome']">Home</a></li>
<li><a [routerLink]="['/movies']">Movie List</a></li>
</ul>
</div>
</nav>
<div class="container">
<router-outlet></router-outlet>
</div>
</div>
app
router outlet
[Link]
<div class="container">
<nav class="navbar navbar-default" *ngIf="displayMenu">
<div class="container-fluid">
<a class="navbar-brand">{{pageTitle}}</a>
<ul class="nav navbar-nav">
<li><a [routerLink]="['/welcome']">Home</a></li>
<li><a [routerLink]="['/movies']">Movie List</a></li>
</ul>
</div>
</nav>
<div class="container">
<router-outlet></router-outlet>
</div>
</div>
[Link]
<router-outlet></router-outlet>
app
router outlet
app
router outlet
[Link]
<div class="container">
<nav class="navbar navbar-default">
<div class="container-fluid">
<a class="navbar-brand">{{pageTitle}}</a>
<ul class="nav navbar-nav">
<li><a [routerLink]="['/welcome']">Home</a></li>
<li><a [routerLink]="['/movies']">Movie List</a></li>
</ul>
</div>
</nav>
<div class="container">
<router-outlet></router-outlet>
</div>
</div>
[Link]
<mh-menu></mh-menu>
<div class='container'>
<router-outlet></router-outlet>
</div>
app
router outlet
shell
router outlet
[Link]
[Link]([
{
path: '', component: ShellComponent,
children: [
{ path: 'welcome', component: WelcomeComponent },
{ path: 'movies', component: MovieListComponent },
{ path: '', redirectTo: 'welcome', pathMatch: 'full' }
]
},
{ path: 'login', component: LoginComponent },
{ path: '**', component: PageNotFoundComponent }
])
Preventing
Partial Page Display
Build a Resolver Service
[Link]
@Injectable()
export class MovieResolver implements Resolve<IMovie> {
constructor(private movieService: MovieService) { }
resolve(route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<IMovie> {
const id = [Link]('id');
return [Link](+id);
}
}
Register the Resolver
[Link]
providers: [
MovieService,
MovieResolver
]
Add Resolver to Route
[Link]
{
path: 'movies/:id',
resolve: { movie: MovieResolver },
component: MovieDetailComponent
},
Read Resolver Data
[Link]
ngOnInit(): void {
[Link] = [Link]['movie'];
}
[Link]
{
path: 'movies/:id',
resolve: { movie: MovieResolver },
component: MovieDetailComponent
},
I'm Waiting …
Subscribe to Router Events
[Link]
constructor(private router: Router) {
[Link]((routerEvent: Event) => {
[Link](routerEvent);
});
}
Check Router Events
[Link]
checkRouterEvent(routerEvent: Event): void {
if (routerEvent instanceof NavigationStart) {
[Link] = true;
}
if (routerEvent instanceof NavigationEnd ||
routerEvent instanceof NavigationCancel ||
routerEvent instanceof NavigationError) {
[Link] = false;
}
}
Display the Spinner
[Link]
<span class="glyphicon glyphicon-refresh glyphicon-spin spinner"
*ngIf="loading"></span>
<router-outlet></router-outlet>
What’s going on?
[Link]
[Link]([
{
path: '', component: ShellComponent,
children: [
{ path: 'welcome', component: WelcomeComponent },
{ path: 'movies', component: MovieListComponent },
{ path: '', redirectTo: 'welcome', pathMatch: 'full' }
]
},
{ path: 'login', component: LoginComponent },
{ path: '**', component: PageNotFoundComponent }
], { enableTracing: true })
Guards at the Ready
Build a Guard Service
[Link]
@Injectable()
export class MovieEditGuard implements
CanDeactivate<MovieEditComponent> {
canDeactivate(component: MovieEditComponent): boolean {
if ([Link]) {
const title = [Link] || 'New Movie';
return confirm(`Lose all changes to ${title}?`);
}
return true;
}
}
Register the Guard
[Link]
providers: [
MovieService,
MovieResolver,
MovieEditGuard
]
Add Guard to Route
[Link]
{
path: 'movies/:id/edit',
resolve: { movie: MovieResolver },
canDeactivate: [ MovieEditGuard ],
component: MovieEditComponent
}
Keep Constant
[Link]
@NgModule({
imports: [
[Link]([
{
path: '', component: ShellComponent,
children: [
{ path: 'welcome', component: WelcomeComponent },
{ path: 'movies', component: MovieListComponent },
{ path: 'movies/:id', component: MovieDetailComponent },
{
path: 'movies/:id/edit', component: MovieEditComponent,
children: [
{ path: '', redirectTo: 'info', pathMatch: 'full' },
{ path: 'info', component: MovieEditInfoComponent },
{ path: 'tags', component: MovieEditTagsComponent }
]
},
{ path: '', redirectTo: 'welcome', pathMatch: 'full' }
]
},
{ path: 'login', component: LoginComponent },
{ path: '**', component: PageNotFoundComponent }
])
],
exports: [RouterModule]
})
export class AppRoutingModule { }
[Link]
const appRoutes: Routes = [
{
path: '', component: ShellComponent,
children: [
{ path: 'welcome', component: WelcomeComponent },
{ path: 'movies', component: MovieListComponent },
{ path: 'movies/:id', component: MovieDetailComponent },
{ path: 'movies/:id/edit', component: MovieEditComponent,
children: [
{ path: '', redirectTo: 'info', pathMatch: 'full' },
{ path: 'info', component: MovieEditInfoComponent },
{ path: 'tags', component: MovieEditTagsComponent }
]
},
{ path: '', redirectTo: 'welcome', pathMatch: 'full' }
]
},
{ path: 'login', component: LoginComponent },
{ path: '**', component: PageNotFoundComponent }
];
[Link]
@NgModule({
imports: [
[Link](appRoutes)
],
exports: [RouterModule]
})
export class AppRoutingModule { }
It’s a Feature
What About Feature Modules?
[Link]
const appRoutes: Routes = [
{
path: '', component: ShellComponent,
children: [
{ path: 'welcome', component: WelcomeComponent },
{ path: 'movies', component: MovieListComponent },
{ path: 'movies/:id', component: MovieDetailComponent },
{ path: 'movies/:id/edit', component: MovieEditComponent,
children: [
{ path: '', redirectTo: 'info', pathMatch: 'full' },
{ path: 'info', component: MovieEditInfoComponent },
{ path: 'tags', component: MovieEditTagsComponent }
]
},
{ path: '', redirectTo: 'welcome', pathMatch: 'full' }
]
},
{ path: 'login', component: LoginComponent },
{ path: '**', component: PageNotFoundComponent }
];
[Link]
const movieRoutes: Routes = [
{ path: 'movies', component: MovieListComponent } ,
{ path: 'movies/:id', component: MovieDetailComponent },
{ path: 'movies/:id/edit', component: MovieEditComponent,
children: [
{ path: '', redirectTo: 'info', pathMatch: 'full' },
{ path: 'info', component: MovieEditInfoComponent },
{ path: 'tags', component: MovieEditTagsComponent }
]
}
];
@NgModule({
imports: [ ...,
[Link](movieRoutes) // Won’t work!
], ...
})
export class MovieModule { }
imports: [
MovieModule, Merged Routes
[Link](appRoutes)
] { path: 'movies', component: MovieListComponent } ,
{ path: 'movies/:id', component: MovieDetailComponent },
{ path: 'movies/:id/edit', component: MovieEditComponent,
children: [
{ path: '', redirectTo: 'info', pathMatch: 'full' },
{ path: 'info', component: MovieEditInfoComponent },
{ path: 'tags', component: MovieEditTagsComponent }
]
}
{ path: '', component: ShellComponent,
children: [
{ path: 'welcome', component: WelcomeComponent },
{ path: '', redirectTo: 'welcome', pathMatch: 'full' }
]
},
{ path: 'login', component: LoginComponent },
{ path: '**', component: PageNotFoundComponent }
Lazy Loading
From: Angular Routing course on Pluralsight
loadChildren
[Link]
{
path: '', component: ShellComponent,
children: [
{ path: 'welcome', component: WelcomeComponent },
{
path: 'movies',
loadChildren: './movies/[Link]#MovieModule'
},
{ path: '', redirectTo: 'welcome', pathMatch: 'full' }
]
},
{ path: 'login', component: LoginComponent },
{ path: '**', component: PageNotFoundComponent }
[Link]
const movieRoutes: Routes = [
{ path: '', component: MovieListComponent } ,
{ path: ':id', component: MovieDetailComponent },
{ path: ':id/edit', component: MovieEditComponent,
children: [
{ path: '', redirectTo: 'info', pathMatch: 'full' },
{ path: 'info', component: MovieEditInfoComponent },
{ path: 'tags', component: MovieEditTagsComponent }
]
}
];
@NgModule({
imports: [ ...,
[Link](movieRoutes)
], ...
})
export class MovieModule { }
For More Information
Twitter: @deborahkurata
Code: [Link]
routing
Slides: [Link]/ngConf18
Course: [Link]/Angular-routing