路由的作用是分隔應用為不同的區塊,每個區塊基於匹配當前URL的規則。 路由可以分為服務端與客戶端兩種,服務端以Express.js為例: 服務端接收請求並路由至一個控制器(controller),控制器執行指定的操作(action)。 客戶端的路由在概念上與服務端相似,其好處是不需要每次URL地址變 ...
路由的作用是分隔應用為不同的區塊,每個區塊基於匹配當前URL的規則。
路由可以分為服務端與客戶端兩種,服務端以Express.js為例:
var express = require('express');
var router = express.Router();
// define the about route
router.get('/about', function(req, res) {
res.send('About us');
});
服務端接收請求並路由至一個控制器(controller),控制器執行指定的操作(action)。
客戶端的路由在概念上與服務端相似,其好處是不需要每次URL地址變化都將路由請求發送至服務端。
客戶端路由有兩種實現方式:使用傳統的錨(anchor)標簽與HTML5客戶端路由。第一種方式也被稱為hash-based路由,URL的形式如這般:http://something/#/about
。第二種依賴HTML5的history.pushState
方法,缺點是老舊的瀏覽器不支持這種方式,以及伺服器也必須支持基於HTML5的路由。Angular官方推薦的是後者。
使用Angular路由的方法,首先要導入相關類庫:
import {
RouterModule,
Routes
} from '@angular/router';
再配置路由規則:
const routes: Routes = [
// basic routes
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: 'contact', component: ContactComponent },
{ path: 'contactus', redirectTo: 'contact' }
]
path指定路由所要處理的URL,component綁定相關的組件,redirectTo重定向至已知的路由。
最後在NgModule中引入RouterModule模塊及預設的路由:
imports: [
BrowserModule,
FormsModule,
HttpModule,
RouterModule.forRoot(routes), // <-- routes
// added this for our child module
ProductsModule
]
Angular預設的路由策略是PathLocationStrategy,即基於HTML5的路由。如果想使用HashLocationStrategy,需要在代碼里額外申明。
providers: [
{ provide: LocationStrategy, useClass: HashLocationStrategy }
]
路由上可以帶參數,通過/route/:param
的形式。不過這種情況下需要導入ActivatedRoute。
import { ActivatedRoute } from '@angular/router';
const routes: Routes = [
{ path: 'product/:id', component: ProductComponent }
];
export class ProductComponent {
id: string;
constructor(private route: ActivatedRoute) {
route.params.subscribe(params => { this.id = params['id']; });
}
}
想在頁面中添加跳轉鏈接的話,可以使用[routerLink]指令:
<div class="page-header">
<div class="container">
<h1>Router Sample</h1>
<div class="navLinks">
<a [routerLink]="['/home']">Home</a>
<a [routerLink]="['/about']">About Us</a>
<a [routerLink]="['/contact']">Contact Us</a>
|
<a [routerLink]="['/products']">Products</a>
<a [routerLink]="['/login']">Login</a>
<a [routerLink]="['/protected']">Protected</a>
</div>
</div>
</div>
而如果想要用模板頁面的話,則需要
<div id="content">
<div class="container">
<router-outlet></router-outlet>
</div>
</div>
頁面中的router-outlet元素即是每個路由綁定的組件所渲染內容的位置。
複雜的頁面可能還會需要用到嵌套路由:
const routes: Routes = [
//nested
{ path: 'products',
component: ProductsComponent,
chilren: [
{ path: '', redirectTo: 'main', pathMatch: 'full' },
{ path: 'main', component: MainComponent },
{ path: 'more-info', component: MoreInfoComponent },
{ path: ':id', component: ProductComponent },
]
}
]
路由的建立是經由相對路徑來構成,所以需要有配置其基礎路徑位置的地方,一般這個配置會放在index.html頁面的base元素中。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Routing</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root>Loading...</app-root>
</body>
</html>
最常見的寫法就是<base href="/">
。
同時還可以在NgModule中以代碼實現,兩者效果等效:
providers: [
{ provide: APP_BASE_HREF, useValue: '/' } // <--- this right here
]
切換路由時可能會有要求額外的處理工作,比如認證。這種場景下,路由的構子可以派上用處。
import { Injectable } from '@angular/core';
@Injectable()
export class AuthService {
login(user: string, password: string): boolean {
if (user === 'user' && password === 'password') {
localStorage.setItem('username', user);
return true;
}
return false;
}
logout(): any {
localStorage.removeItem('username');
}
getUser(): any {
return localStorage.getItem('username');
}
isLoggedIn(): boolean {
return this.getUser() !== null;
}
}
export const AUTH_PROVIDERS: Array<any> = [
{ provide: AuthService, useClass: AuthService }
];
import { Injectable } from '@angular/core';
import {
CanActivate,
ActivatedRouteSnapshot,
RouterStateSnapshot
} from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { AuthService } from './auth.service';
@Injectable()
export class LoggedInGuard implements CanActivate {
constructor(private authService: AuthService) {}
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
const isLoggedIn = this.authService.isLoggedIn();
console.log('canActivate', isLoggedIn);
return isLoggedIn;
}
}
import { AUTH_PROVIDERS } from './auth.service';
import { LoggedInGuard } from './logged-in.guard';
const routes: Routes = [
{
path: 'protected',
component: ProtectedComponent,
canActivate: [ LoggedInGuard ]
},
];
上述例子中,當路由至'protected'地址時,'LoggedInGuard'處理類會進入路由調用環路,依據是否已登陸這一信息來決定此次路由是否有效。