9. Component Router
Routes:
AppModule:
import { RouterModule, Routes } from '@angular/router';
const appRoutes: Routes = [
{ path: '**', component: WelcomeComponent } // <-- catchAll route
];
imports: [
BrowserModule,
HttpModule,
ReactiveFormsModule,
FormsModule,
RouterModule.forRoot(appRoutes)
]
AppComponent:
<router-outlet></router-outlet>
routerLinks:
const appRoutes: Routes = [
{ path: 'article', component: ArticleComponent },
{ path: 'princess', component: PrincessComponent },
{ path: '**', component: WelcomeComponent }
];
<h2>Root/Routing Component</h2>
<a [routerLink]="''">Welcome</a>
<a [routerLink]="'princess'">Princess</a>
<a [routerLink]="'article'">Article</a>
<router-outlet></router-outlet>
Router navigation:
import { Component } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-root',
template: `
<div class="row">
<h1>{{ title }}</h1>
<div class="col-md-6">
<h2>Root/Routing Component</h2>
<button (click)="visitPrincess()">Princess</button>
<button (click)="visitArticle()">Article</button>
<router-outlet></router-outlet>
</div>
<div class="col-md-6">
<app-princess></app-princess>
</div>
</div>
`,
styles: ['']
})
export class AppComponent {
title = 'Angular 4 Love Affair';
constructor(private router: Router) { }
visitPrincess(): void {
this.router.navigate(['princess']);
}
visitArticle(): void {
this.router.navigate(['article']);
}
}
LocationStrategy:
PathLocationStrategy: http://localhost:4200/princess
HashLocationStrategy: http://localhost:4200/#/princess
AppModule:
import { LocationStrategy, HashLocationStrategy, PathLocationStrategy } from '@angular/common';
providers: [AuthenticationService, PublishSubscribeService, APIService,
{provide: LocationStrategy, useClass: HashLocationStrategy}]
routerLinkActive:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<div class="row">
<h1>{{ title }}</h1>
<div class="col-md-6">
<h2>Root/Routing Component</h2>
<a [routerLink]="''" [routerLinkActive]="'active-link'">Welcome</a>
<a [routerLink]="'princess'" [routerLinkActive]="'active-link'">Princess</a>
<a [routerLink]="'article'" [routerLinkActive]="'active-link'">Article</a>
<router-outlet></router-outlet>
</div>
<div class="col-md-6">
<app-princess></app-princess>
</div>
</div>
`,
styles: [`
.active-link {
color: red;
text-transform: uppercase;
}
`]
})
export class AppComponent {
title = 'Angular 4 Love Affair';
}
routerLinkActiveOptions:
<a [routerLink]="''" [routerLinkActive]="'active-link'" [routerLinkActiveOptions]="{exact: true}" >Welcome</a>
<a [routerLink]="'princess'" [routerLinkActive]="'active-link'" [routerLinkActiveOptions]="{exact: true}">Princess</a>
<a [routerLink]="'article'" [routerLinkActive]="'active-link'" [routerLinkActiveOptions]="{exact: true}">Article</a>
<router-outlet></router-outlet>
Nested views with route parameters and child routes
Parent component routing target for child views:
import { Component } from '@angular/core';
@Component({
selector: 'app-article',
template: `
<h2>Article</h2>
<router-outlet></router-outlet>
`
})
export class ArticleComponent {
}
List and detail components:
import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-article-list',
template: `
<h3>Article List</h3>
<p *ngFor="let articleID of articleIDs">
<a [routerLink]="articleID">
Article {{ articleID }}
</a>
</p>
`
})
export class ArticleListComponent {
articleIDs: number[] = [100, 200, 300, 400, 500];
}
@Component({
selector: 'app-article-detail',
template: `
<h3>Article Detail</h3>
<p>Showing Angular article: {{ articleID }}</p>
<a [routerLink]="'../'">Go Back</a>
`
})
export class ArticleDetailComponent {
articleID: number;
constructor(private activatedRoute: ActivatedRoute) {
activatedRoute.params.subscribe(params => this.articleID = params['articleID']);
}
}
Child routes in AppModule:
const appRoutes: Routes = [
{ path: 'article', component: ArticleComponent,
children: [
{ path: '', component: ArticleListComponent },
{ path: ':articleID', component: ArticleDetailComponent}
]},
{ path: 'princess', component: PrincessComponent },
{ path: '**', component: WelcomeComponent }
];
Refactor detail component with async pipe:
@Component({
selector: 'app-article-detail',
template: `
<h3>Article Detail</h3>
<p>Showing Angular article: {{ ( activatedRoute.params | async ).articleID }}</p>
<a [routerLink]="'../'">Go Back</a>
`
})
export class ArticleDetailComponent {
constructor(private activatedRoute: ActivatedRoute) { }
}
Another way: refer public observable interface and interpolate:
import { Component } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import {Observable } from 'rxjs/Observable';
@Component({
selector: 'app-article-detail',
template: `
<h3>Article Detail</h3>
<p>Showing Angular article: {{ ( params | async ).articleID }}</p>
<a [routerLink]="'../'">Go Back</a>
`
})
export class ArticleDetailComponent {
params: Observable<Params>;
constructor(private activatedRoute: ActivatedRoute) {
this.params = activatedRoute.params;
}
}
Routing arrays && matrix URL parameters
<a [routerLink]="['article', {listData: 'ng4Rocks'}]" [routerLinkActive]="'active-link'" [routerLinkActiveOptions]="{exact: true}">Article</a>
Extract data from ActivatedRoute params:
@Component({
selector: 'app-article-list',
template: `
<h3>Article List</h3>
<p *ngFor="let articleID of articleIDs">
<a [routerLink]="articleID">
Article {{ articleID }}
</a>
</p>
`
})
export class ArticleListComponent {
articleIDs: number[] = [100, 200, 300, 400, 500];
constructor(private activatedRoute: ActivatedRoute) {
activatedRoute.params.subscribe(params => {
console.log('List params:');
console.log(window.location.href);
console.log(params);
});
}
}
AuthenticationGuards
Routes:
const appRoutes: Routes = [
{ path: 'login', component: LogInComponent },
{ path: 'logout', component: LogOutComponent, canActivate: [ LogOutGuard ] },
{ path: 'profile', component: ProfileComponent, canActivate: [ AuthenticationGuard ] },
{ path: '**', component: WelcomeComponent }
];
Guards:
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthenticationService } from './authentication.service';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class AuthenticationGuard implements CanActivate {
constructor(private authenticationService: AuthenticationService,
private router: Router) { }
canActivate(): Observable<boolean> {
return this.authenticationService.userNameEmitter.map(userName => {
if (!userName) {
this.router.navigate(['login']);
} else {
return true;
}
}).take(1);
}
}
@Injectable()
export class LogOutGuard implements CanActivate {
constructor(private authenticationService: AuthenticationService,
private router: Router) { }
canActivate(): boolean {
this.authenticationService.logout();
this.router.navigate(['']);
return true;
}
}
LogInComponent:
import { Component, OnDestroy } from '@angular/core';
import { Router } from '@angular/router';
import { AuthenticationService } from '../authentication.service';
import { Subscription } from 'rxjs/Subscription';
@Component({
selector: 'app-log-in',
template: `
<h2>LogIn View</h2>
<input #userName>
<button class="btn btn-success" (click)="logIn(userName.value)">LogIn</button>
`
})
export class LogInComponent implements OnDestroy {
private userNameSubscription: Subscription;
constructor(private authenticationService: AuthenticationService,
private router: Router) { }
logIn(newUserName: string): void {
this.authenticationService.login(newUserName);
this.userNameSubscription = this.authenticationService.userNameEmitter
.subscribe(userName => {
if (!!userName) {
this.router.navigate[''];
}
});
}
ngOnDestroy() {
this.userNameSubscription && this.userNameSubscription.unsubscribe();
}
}
ProfileComponent:
import { Component } from '@angular/core';
import { AuthenticationService } from '../authentication.service';
import { Observable } from 'rxjs/Observable';
@Component({
selector: 'app-profile',
template: `
<div class="well">
<h2>Profile View</h2>
UserName: <input #uName value="{{ username | async }}">
<button class="btn btn-danger" (click)="update(uName.value)">Update</button>
</div>
`
})
export class ProfileComponent {
username: Observable<string>;
constructor(private authenticationService: AuthenticationService) {
this.username = authenticationService.userNameEmitter;
}
update(username: string): void {
this.authenticationService.login(username);
}
}
AppComponent:
import { Component } from '@angular/core';
import { AuthenticationService } from './authentication.service';
import { Observable } from 'rxjs/Observable';
@Component({
selector: 'app-root',
template: `
<div class="row">
<h1>{{ title }}</h1>
<div class="col-md-6">
<h2>Root/Routing Component</h2>
<h3 *ngIf="!!(userName | async)">
Hello, {{ userName | async }}.
</h3>
<a [routerLink]="''" [routerLinkActive]="'active-link'" [routerLinkActiveOptions]="{exact: true}">Welcome</a>
<a [routerLink]="'profile'" [routerLinkActive]="'active-link'" [routerLinkActiveOptions]="{exact: true}">Profile</a>
<a *ngIf="!!(!userName | async )" [routerLink]="['login']">Login</a>
<a *ngIf="!!(userName | async)" [routerLink]="['logout']">Logout</a>
<router-outlet></router-outlet>
</div>
<div class="col-md-6">
<app-princess></app-princess>
</div>
</div>
`,
styles: [`
.active-link {
color: red;
text-transform: uppercase;
}
`]
})
export class AppComponent {
title = 'Angular 4 Love Affair';
userName: Observable<string>;
constructor(private authenticationService: AuthenticationService) {
this.userName = authenticationService.userNameEmitter;
}
}