路由是 Angular 應用程序的核心,它加載與所請求路由相關聯的組件,以及獲取特定路由的相關數據。這容許咱們經過控制不一樣的路由,獲取不一樣的數據,從而渲染不一樣的頁面。css
Routes
實際上是一個Route類的數組。html
而Route
的參數以下圖所示,通常狀況下,path
和component
是必選的兩個參數。
好比:path:/a,component:A
則說明,當地址爲/a
時,應該展現組件A
的內容。node
其他類的簡介見下圖:jquery
輸入命令ng new router --routing
新建一個名叫router
的項目,其中--routing
命令參數表明在項目基礎上添加一個路由配置文件app-routingcodule.ts
。typescript
能夠看到路由配置文件已經生成,其初始內容是:npm
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; const routes: Routes = []; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
在項目根路徑下運行命令ng g component home
和ng g component books
來新增home
和books
兩個組件,此時可能會出現too many symbolic links encountered
的錯誤:數組
解決辦法:app
node_modules
目錄npm install -g @angular/cli
在路由配置文件中爲routes
賦值:dom
const routes: Routes = [{path: '', component: HomeComponent}, {path: 'books', component: BooksComponent}];
其中HomeComponent
和BooksComponent
分別對應咱們新增的兩個組件。
另外須要注意path
參數中不須要輸入/
!iphone
打開app.component.html
,輸入一下代碼:
<a [routerLink]="['/']">主頁</a> <a [routerLink]="['/books']">書籍</a> <router-outlet></router-outlet>
啓動項目就能夠看到效果了:
要使用Router
對象,首先須要在控制器文件中定義它,通常是在構造函數constructor
中傳遞相關參數:
constructor(private router: Router) { }
這樣就能夠在控制器中使用router
來跳轉路由了,好比寫個方法toBookDetails
:
toBookDetails() { router.navigate(['/books']); }
如何調用呢,在模板文件中經過定義一個按鈕並用(click)
來綁定便可:
<input type="button" value="書籍" (click)="toBookDetails()">
效果以下:
以上咱們都假設輸入的路徑都是存在的,可是假如用戶不當心輸入了一個不存在的路徑,咱們有義務給予一些善意的提醒,如何實現呢?這就要使用通配符了,其用法以下:
首先建立一個新增模塊err404
模塊,用於輸入不存在的路徑時展現,命令以下:
ng g component err404
;
而後在路由配置文件中添加一條路由信息:
const routes: Routes = [ {path: '', component: HomeComponent}, {path: 'books', component: BooksComponent}, {path: '**', component: Err404Component} ];
程序會根據用戶定義的路由順序依次匹配,當全部明確指定的路由均不知足匹配條件時,纔會進入Err404Component
組件,因爲是依次匹配,因此將其放入最後。
最後,輸入http://localhost:4200/test
,結果以下:
參數傳遞的幾種方式:
/product?id=1&name=iphone => ActivatedRoute.queryParams[id]
;{path:/product/:id} => /product/1 => ActivatedRoute.params[id]
;{path:/product,component:ProductComponent,data:[{madeInChina:true}]} => ActivatedRoute.data[0][madeInChina]
;與注入Router對象同樣,將其置於組件控制器的構造函數中,此處以BooksComponent
爲例併爲其增長一個bookname
的屬性,讓其在初始化時直接顯示屬性bookname
的值:
import {Component, OnInit} from '@angular/core'; import {ActivatedRoute, Params} from '@angular/router'; @Component({ selector: 'app-books', templateUrl: './books.component.html', styleUrls: ['./books.component.css'] }) export class BooksComponent implements OnInit { private bookname: string; constructor(private activatedRout: ActivatedRoute) { } ngOnInit() { // 普通方式傳參的快照獲取方式 this.bookname = this.activatedRout.snapshot.queryParams['bookname']; } }
其中snapshot
表示快照方式,還可使用訂閱subscribe
方式,下面再說。
在模板文件app.component.html
中爲/books
所在的<a>
標籤中添加屬性[queryParams]
:
<a [routerLink]="['/books']" [queryParams]="{bookname:'《活着》'}">書籍</a>
此時點擊顯示的路由信息則爲:
爲了更明確起見,在books組件的模板文件中添加綁定信息:
最終呈現效果:
首先須要在路由配置文件中預約義參數的名稱:
而後在<a>
中直接傳遞值:
<a [routerLink]="['/books','《活着》']">書籍</a>
此時點擊連接時所呈現的路徑變爲:
最後,經過params方法來獲取參數的值:
// rest方式傳參的快照獲取方式 this.bookname = this.activatedRout.snapshot.params['bookname'];
這樣就能夠呈現同上面同樣的效果的內容了。那麼,如何經過方法來傳遞rest形式的參數呢?其實同<a>
的格式同樣:
toBookDetails() { this.router.navigate(['/books', '《簡愛》']); }
此時我傳入的參數是《簡愛》
,在連接主頁
和連接書籍
或者在連接主頁
和按鈕書籍
之間切換點擊時,內容的顯示是沒有問題的,可是在連接書籍
和按鈕書籍
之間切換點擊時,底下展現的內容不發生變化,這時就要用到參數訂閱的方式來實時呈現了:
// rest方式傳參的訂閱獲取方式 this.activatedRout.params.subscribe((params: Params) => this.bookname = params['bookname']);
這種傳參是靜態的,假設此時須要添加一個參數isChineseVersion
來表示是不是中文版,其配置形式以下:
此時在book
組件控制器中新增一個boolean
類型參數isChineseVersion
,並在初始化方法ngOnInit
中爲其賦值:
this.isChineseVersion = this.activatedRout.snapshot.data[0]['isChineseVersion'];
,最後在模板文件中綁定便可:
<p> is Chinese Version: {{isChineseVersion}} </p>
在用戶訪問一個特定地址時,將其重定向到另外一個指定的地址。
此處先將全部指向HomeComponent
的根路徑所有改成/home
,包括路由配置文件和<a>
。
此時雖然指定了當路徑爲/home
時才指向HomeComponent
組件,但若是我但願訪問根路徑時直接展現HomeComponent
的內容是怎麼辦,重定向路由能夠幫助咱們實現,語法以下:
const routes: Routes = [ {path: '', redirectTo: '/home', pathMatch: 'full'}, {path: 'home', component: HomeComponent}, {path: 'books/:bookname', component: BooksComponent, data: [{isChineseVersion: true}]}, {path: '**', component: Err404Component} ];
須要注意的是,pathMatch屬性的值,當爲full
時,表示只有當路徑爲根路徑時才指向HomeComponent
組件,它還有另外一個值:prefix
,意思是匹配前綴,試了以後發現沒有用,好比
{path: 'hh', redirectTo: '/home', pathMatch: 'prefix'}
,當訪問的路徑爲http://localhost:4200/h
或者http://localhost:4200/hhh
時都不會呈現主頁內容而是直接跳到Err404
的內容,此處存疑。
子路由定義的格式以下:
上圖中子路由的意思是,當訪問/home/
時,展現HomeComponent
和XxxComponent
的內容,當訪問/home/yyy
時,展現HomeComponent
和YyyComponent
的內容。
在書籍模塊的模板下方,增長兩個連接,《簡愛》的中文簡介和英文簡介,點擊時分別顯示對應語言的簡介,默認顯示中文簡介。
introduction_cn
=>ng g component introduction_cn
和introduction_en
=>ng g component introduction_en
{ path: 'books/:bookname', component: BooksComponent, children: [ {path: '', component: IntroductionCnComponent}, {path: 'en', component: IntroductionEnComponent} ] }
<p> books works! </p> <p> query book is name of {{bookname}} </p> <a [routerLink]="['./']">中文簡介</a> <a [routerLink]="['./en']">英文簡介</a> <router-outlet></router-outlet>
以上的例子中都是一個路由來操控頁面的顯示內容,可是實際上頁面老是有多個模塊,如何來分配各自區域的顯示控制呢?這時就要用到多路由了,好比在上面的例子中,在主界面上只定義了一個<router-outlet></router-outlet>
,當咱們定義多個路由時就須要加以區分,實現方式很簡單,在<router-outlet>
上加一個name
屬性並賦值便可,好比:<router-outlet name = "a"></router-outlet>
,此時a
就是此路由定位器的標識,當不定義name
屬性時,其名稱默認爲primary
。
下面經過實際例子作個簡單演示:
開始諮詢
和結束諮詢
,當點擊開始諮詢
時頁面右側顯示文本域,同時左側顯示HomeComponent
組件內容,當點擊結束諮詢
時文本域消失,頁面左側仍然顯示原有內容。HomeComponent
/BooksComponent
,以主頁組件爲例演示:使用div元素將原有內容包裹,並定義class
值爲home
<div class="home"> <p> home works! </p> </div>
定義樣式:
.home { background-color: yellowgreen; height: 750px; width: 70%; float: left; box-sizing: border-box; }
同理定義BooksComponent
模板及樣式文件。
ng g component advise
<textarea placeholder="請輸入內容" class="advise"></textarea>
.advise { background-color: green; height: 750px; width: 30%; float: left; box-sizing: border-box; }
advise
在路由配置文件中新增路由並制定定位器:
新增開始諮詢
連接並指定定位器
指定定位器仍然經過routerLink
,格式:<a [routerLink]="[{outlets:{primary:'home', advise:'advise'}}]">開始諮詢</a>
表示點擊連接時,沒有名字(前面說了,它的默認名字叫primary
)的定位器處顯示路徑爲/home
即組件是HomeComponent
的內容,而名叫advise
定位器處顯示路徑爲/advise
即組件是AdviseComponent
的內容。
同理,定義結束諮詢的:<a [routerLink]="[{outlets:{advise:null}}]">書籍</a>
添加名叫advise
的定位器
效果
路由守衛通常涉及三個類:CanActivate
,CanDeactivate
,Resolve
.
CanActivate:決定是否有權限進入某路由;
CanDeactivate:決定是否能夠離開某路由;
Resolve:初始化某個組件中的被Resolve綁定的類型參數;
新建ts文件:PermissionGuard.ts
import {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router'; import {Observable} from 'rxjs/Observable'; import * as _ from 'lodash'; export class PermissionGuard implements CanActivate { canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | Observable<boolean> | Promise<boolean> { const rand = _.random(0, 5); console.log(rand); return rand % 2 === 0; } }
在路由配置文件中增長canActivate
參數:
const routes: Routes = [ {path: '', redirectTo: '/home', pathMatch: 'full'}, {path: 'home', component: HomeComponent}, {path: 'advise', component: AdviseComponent, outlet: 'advise'}, /*{path: 'books/:bookname', component: BooksComponent, data: [{isChineseVersion: true}]},*/ { path: 'books/:bookname', component: BooksComponent, children: [ {path: '', component: IntroductionCnComponent}, {path: 'en', component: IntroductionEnComponent} ], canActivate: [PermissionGuard] }, {path: '**', component: Err404Component} ];
在模塊配置文件app.moudle.ts
中的providers
參數中增長實現了CanActivate
接口的值:
providers: [PermissionGuard]
目的是實例化PermissionGuard
。
此時,再次點擊連接書籍
時會先判斷生成的隨機數除2時的餘數,餘0則可進入,不然被阻止。
需求:
當諮詢窗口已被打開,而且已經輸入了內容,此時忽然點擊結束,會被阻止,相似博客編輯到一半忽然訪問其餘網站時會給出提示同樣。
新建ts文件LeaveGuard.ts
:
import {CanDeactivate} from '@angular/router'; import {AdviseComponent} from '../advise/advise.component'; import {Observable} from 'rxjs/Observable'; export class LeaveGuard implements CanDeactivate<AdviseComponent> { /** * 下面這段代碼是自帶實現,暫時注掉不用它。 * canDeactivate(component: AdviseComponent, * currentRoute: ActivatedRouteSnapshot, * currentState: RouterStateSnapshot, * nextState?: RouterStateSnapshot) * : boolean | Observable<boolean> | Promise<boolean> { * throw new Error("Method not implemented."); * } * @param advise * @returns {boolean} */ canDeactivate(advise: AdviseComponent): boolean | Observable<boolean> | Promise<boolean> { return advise.isEmpty(); } }
能夠看到CanDeactivate
不一樣於CanActivate
,使用時須要輸入泛型類,用以綁定數據。代碼中使用了isEmpty()
,它是諮詢組件AdviseComponent
中的一個方法,表示當輸入的諮詢內容爲空時返回真,代碼以下:
import {Component, OnInit} from '@angular/core'; import * as $ from 'jquery'; @Component({ selector: 'app-advise', templateUrl: './advise.component.html', styleUrls: ['./advise.component.css'] }) export class AdviseComponent implements OnInit { constructor() { } ngOnInit() { } isEmpty(): boolean { const adviseVal = $('textarea').val(); console.log(adviseVal); return adviseVal === ''; } }
在路由配置文件中爲組件AdviseComponent
添加canDeactivate
屬性並賦值:
{ path: 'advise', component: AdviseComponent, outlet: 'advise', canDeactivate: [LeaveGuard] }
在模塊文件app.module.ts
參數providers
新增LeaveGuard
用以實例化:
providers: [PermissionGuard, LeaveGuard]
爲方便演示,先將路由配置文件中關於BookComponent
的canActivate
參數注掉,防止干擾。
在books.componnet.ts
中新建類Book
,將屬性bookname
替換名爲book
類型爲Book
的成員變量,添加對對此成員變量的訂閱方法:
import {Component, OnInit} from '@angular/core'; import {ActivatedRoute} from '@angular/router'; @Component({ selector: 'app-books', templateUrl: './books.component.html', styleUrls: ['./books.component.css'] }) export class BooksComponent implements OnInit { public book: Book; private isChineseVersion: boolean; constructor(private activatedRout: ActivatedRoute) { } ngOnInit() { // 普通方式傳參的快照獲取方式 // this.bookname = this.activatedRout.snapshot.queryParams['bookname']; // rest方式傳參的快照獲取方式 // this.bookname = this.activatedRout.snapshot.params['bookname']; // rest方式傳參的訂閱獲取方式 // this.activatedRout.params.subscribe((params: Params) => this.book.bookname = params['bookname']); this.activatedRout.data.subscribe((data: { book: Book }) => this.book = data.book); // 路由配置方式傳參的獲取方式 // this.isChineseVersion = this.activatedRout.snapshot.data[0]['isChineseVersion']; } } export class Book { public bookname: string; public author?: string; public private?: number; constructor(bookname: string) { this.bookname = bookname; } }
修改books.component.html
中query book is name of {{bookname}}
=> query book is name of {{book?.bookname}}
新建BookResolve.ts
:
import {ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot} from '@angular/router'; import {Book} from '../books/books.component'; import {Observable} from 'rxjs/Observable'; import {Injectable} from '@angular/core'; @Injectable() export class BookResolve implements Resolve<Book> { constructor(private router: Router) { } resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Book | Observable<Book> | Promise<Book> { const bookname = route.params['bookname']; if (bookname === '《活着》' || bookname === '《簡愛》') { console.log('當前輸入書名爲:' + bookname); return new Book('《簡愛》'); } else { this.router.navigate(['/home']); return null; } } }
在路由控制文件中爲組件BooksComponent
添加resolve
參數並賦值:resolve: {book: BookResolve}
在模塊文件app.module.ts
參數providers
新增BookResolve
用以實例化:
providers: [PermissionGuard, LeaveGuard, BookResolve]
http://pan.baidu.com/s/1bpNdgFt
使用前先運行ci.bat
~