路由是 Angular 應用程序的核心,它加載與所請求路由相關聯的組件,以及獲取特定路由的相關數據。這容許咱們經過控制不一樣的路由,獲取不一樣的數據,從而渲染不一樣的頁面。html
yarn add @angular/router
# OR
npm i --save @angular/router
以上命令執行後,將會自動下載 @angular/router
模塊到 node_modules
文件夾中。正常的angular項目通常不須要進行此步,在構建想的時候已經將此包下載。node
咱們須要作的最後一件事,是將 <base>
標籤添加到咱們的 index.html
文件中。路由須要根據這個來肯定應用程序的根目錄。例如,當咱們轉到 http://example.com/page1
時,若是咱們沒有定義應用程序的基礎路徑,路由將沒法知道咱們的應用的託管地址是 http://example.com
仍是 http://example.com/page1
。npm
這件事操做起來很簡單,只需打開項目中的 index.html
文件,添加相應的 <base>
標籤,具體以下:數組
1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>Mostlove</title> 6 <base href="/"> 7 8 <meta name="viewport" content="width=device-width, initial-scale=1"> 9 <link rel="icon" type="image/x-icon" href="favicon.ico"> 10 </head>
以上配置信息告訴 Angular 路由,應用程序的根目錄是 /
。若是沒有此處的配置Angular將沒法進行路由功能。app
1.路由相關配置less
路由類設置 dom
/*路由基本模型*/
/*導入RouterModule,Routes類型*/
import { RouterModule, Routes } from '@angular/router'; import { LoginComponent } from "./login/login.component"; /*定義路由const表示不可改變*/ const routers: Routes = [ /* path:字符串,表示默認登入, path爲路徑 /login component:組件 component:組件 pathMatch:爲字符串默認爲前綴匹配 "prefix"; "full" 爲徹底匹配。 redirectTo:指向爲路徑,既path outlet:字符串,路由目標,面對多個路由的狀況 children:Routes 子路由相關 */ { path: '', component: LoginComponent }, // path:路徑 /detail/1 :id表明參數相關 { path: 'detail/:id', component: LoginComponent }, // 懶加載子模塊, 子模塊須要配置路由設置啓動子組件,若是這樣設置了路由,須要在子模塊中再定義路由 { path: 'other', loadChildren:"./demo/demo.module#demoModule" }, // 重定向,路徑爲** 表示不能識別的路徑信息,重定向到相關路徑下 { path: '**', pathMatch: 'full', redirectTo: '' } ]; /*將路由設置導出,子模塊中的路由使用 forChild 而不是 forRoot*/ export const appRouter = RouterModule.forRoot(routers);
ngModule設置
編輯器
@NgModule({ declarations: [ ...... ], imports: [ ...... appRouter ] })
組件模板設置
ide
<router-outlet></router-outlet>
2.多路由處理
函數
{ path: 'news', outlet: 'let1', component: }, { path: 'news', outlet: 'let2', component: }, //模板中NewsComponentNews2Cmponent
<router-outlet name="let1"></router-outlet> <router-outlet name="let2"></router-outlet>
訪問 /news/
時同時加載 NewsComponent
和 News2Cmponent
兩個組件
3.路有連接以及組件中調用路由方法使用
<a routerLink="/detail/1" routerLinkActive="active">detail</a>
<a [routerLink]="['/detail', news.id]">{{news.title}}</a>
<a [routerLink]="[{ outlets: { let2: ['news'] } }]">Contact</a>
routerLinkActive="active"
即在本路由激活時添加樣式 .active
或者:
this.router.navigate(['/detail', this.news.id]) this.router.navigate([{ outlets: { let2: null }}]);
其中:navigateByUrl 方法指向完整的絕對路徑
4.路由守衛(適用於後臺管理等須要登陸才能使用的模塊)
import { Injectable } from '@angular/core'; import { CanActivate } from '@angular/router'; @Injectable() export class AuthService implements CanActivate { canActivate() { // 這裏判斷登陸狀態, 返回 true 或 false return true; } }
在路由配置中的設置
{ path: '', component: LoginComponent, canActivate:[LoginComponent] },
5.退出守衛(適合於編輯器修改後的保存提示等場景)
import { Injectable } from '@angular/core'; import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; // CanDeactivateComponent 是定義的接口,見下段代碼 import { CanDeactivateComponent } from './can-deactivate.omponent'; @Injectable() export class DeacService implements CanDeactivate<CanDeactivateComponent> { canDeactivate( canDeactivateComponent: CanDeactivateComponent, activatedRouteSnapshot: ActivatedRouteSnapshot, routerStateSnapshot: RouterStateSnapshot ) { // 目標路由和當前路由 console.log(activatedRouteSnapshot); console.log(routerStateSnapshot); // 判斷並返回 return canDeactivateComponent.canDeactivate ? canDeactivateComponent.canDeactivate() : true } }..
// 接口組件, 返回 true 或 false 如表單發生改變則調用對話框服務
export interface CanDeactivateComponent { canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean; }
路由配置
{
path: ..., canDeactivate: [DeacService], component: ... }
模塊中添加服務
providers: [ DeactivateGuardService ]
要使用路由,咱們須要在 AppModule
模塊中,導入 RouterModule
。具體以下:
1 mport { BrowserModule } from '@angular/platform-browser'; 2 import { NgModule } from '@angular/core'; 3 import { FormsModule } from '@angular/forms'; 4 import { HttpModule } from '@angular/http'; 5 import { RouterModule } from "@angular/router";
此時咱們的路由還不能正常工做,由於咱們還未配置應用程序路由的相關信息。RouterModule
對象爲咱們提供了兩個靜態的方法:forRoot()
和 forChild()
來配置路由信息。
RouterModule.forRoot() 方法用於在主模塊中定義主要的路由信息,經過調用該方法使得咱們的主模塊能夠訪問路由模塊中定義的全部指令。接下來咱們來看一下如何使用 forRoot()
:
1 @NgModule({ 2 declarations: [ 3 AppComponent, 4 NavigationComponent, 5 SideComponent, 6 // DisplayComponent 7 ], 8 imports: [ 9 BrowserModule, 10 FormsModule, 11 HttpClientModule, 12 HttpModule, 13 RouterModule.forRoot(ROUTES), 14 // SideModule 15 ],
咱們經過使用 const
定義路由的配置信息,而後把它做爲參數調用 RouterModule.forRoot()
方法,而不是直接使用 RouterModule.forRoot([...])
這種方式,這樣作的好處是方便咱們在須要的時候導出 ROUTES
到其它模塊中。
RouterModule.forChild() 與 Router.forRoot() 方法相似,但它只能應用在特性模塊中。
友情提示:根模塊中使用 forRoot()
,子模塊中使用 forChild()
這個功能很是強大,由於咱們沒必要在一個地方(咱們的主模塊)定義全部路由信息。反之,咱們能夠在特性模塊中定義模塊特有的路由信息,並在必要的時候將它們導入咱們主模塊。
1 import { NgModule } from '@angular/core'; 2 import { CommonModule } from '@angular/common'; 3 import { Routes, RouterModule } from '@angular/router'; 4 5 export const ROUTES: Routes = []; 6 7 @NgModule({ 8 imports: [ 9 CommonModule, 10 RouterModule.forChild(ROUTES) 11 ], 12 // ... 13 }) 14 export class ChildModule {}
經過以上示例,咱們知道在主模塊和特性模塊中,路由配置對象的類型是同樣的,區別只是主模塊和特性模塊中需調用不一樣的方法,來配置模塊路由。接下來咱們來介紹一下如何配置 ROUTES
對象。
咱們定義的全部路由都是做爲 ROUTES
數組中的對象。首先,爲咱們的主頁定義一個路由:
1 import { Routes, RouterModule } from '@angular/router'; 2 3 import { HomeComponent } from './home/home.component'; 4 5 export const ROUTES: Routes = [ 6 { path: '', component: HomeComponent } 7 ];
示例中咱們經過 path
屬性定義路由的匹配路徑,而 component
屬性用於定義路由匹配時須要加載的組件。
友情提示:咱們使用 path: ''
來匹配空的路徑,例如:https://yourdomain.com
配置完路由信息後,下一步是使用一個名爲 router-outlet
的指令告訴 Angular 在哪裏加載組件。當 Angular 路由匹配到響應路徑,併成功找到須要加載的組件時,它將動態建立對應的組件,並將其做爲兄弟元素,插入到 router-outlet
元素中。
在咱們 AppComponent
組件中,咱們能夠在任意位置插入 router-outlet
指令:咱們如今已經創建了應用程序的主路由,咱們能夠進一步瞭解路由的其它配置選項。
到目前爲止咱們已經介紹的內容只是一個開始 ,接下來咱們來看看其它一些選項和功能。
若是路由始終是靜態的,那沒有多大的用處。例如 path: ''
是加載咱們 HomeComponent
組件的靜態路由。咱們將介紹動態路由,基於動態路由咱們能夠根據不一樣的路由參數,渲染不一樣的頁面。
例如,若是咱們想要在我的資料頁面根據不一樣的用戶名顯示不一樣的用戶信息,咱們可使用如下方式定義路由:
1 import { HomeComponent } from './home/home.component'; 2 import { ProfileComponent } from './profile/profile.component'; 3 4 export const ROUTES: Routes = [ 5 { path: '', component: HomeComponent }, 6 { path: '/profile/:username', component: ProfileComponent } 7 ];
這裏的關鍵點是 :
,它告訴 Angular 路由,:username
是路由參數,而不是 URL 中實際的部分。
友情提示:若是沒有使用 :
,它將做爲靜態路由,僅匹配 /profile/username
路徑
如今咱們已經創建一個動態路由,此時最重要的事情就是如何獲取路由參數。要訪問當前路由的相關信息,咱們須要先從 @angular/router
模塊中導入 ActivatedRoute
,而後在組件類的構造函數中注入該對象,最後經過訂閱該對象的 params
屬性,來獲取路由參數,具體示例以下:
1 import { Component, OnInit } from '@angular/core'; 2 import { ActivatedRoute } from '@angular/router'; 3 4 @Component({ 5 selector: 'profile-page', 6 template: ` 7 <div class="profile"> 8 <h3>{{ username }}</h3> 9 </div> 10 ` 11 }) 12 export class SettingsComponent implements OnInit { 13 username: string; 14 constructor(private route: ActivatedRoute) {} 15 ngOnInit() { 16 this.route.params.subscribe((params) => this.username = params.username); 17 } 18 }
介紹完動態路由,咱們來探討一下如何建立 child routes
。
實際上每一個路由都支持子路由,假設在咱們 /settings
設置頁面下有 /settings/profile
和 /settings/password
兩個頁面,分別表示我的資料頁和修改密碼頁。
咱們可能但願咱們的 / settings
頁面擁有本身的組件,而後在設置頁面組件中顯示 / settings/profile
和 / settings/password
頁面。咱們能夠這樣作:
1 import { SettingsComponent } from './settings/settings.component'; 2 import { ProfileSettingsComponent } from './settings/profile/profile.component'; 3 import { PasswordSettingsComponent } from './settings/password/password.component'; 4 5 export const ROUTES: Routes = [ 6 { 7 path: 'settings', 8 component: SettingsComponent, 9 children: [ 10 { path: 'profile', component: ProfileSettingsComponent }, 11 { path: 'password', component: PasswordSettingsComponent } 12 ] 13 } 14 ];
在這裏,咱們在 setttings
路由中定義了兩個子路由,它們將繼承父路由的路徑,所以修改密碼頁面的路由匹配地址是 /settings/password
,依此類推。
接下來,咱們須要作的最後一件事是在咱們的 SettingsComponent
組件中添加 router-outlet
指令,由於咱們要在設置頁面中呈現子路由。若是咱們沒有在 SettingsComponent
組件中添加 router-outlet
指令,儘管 /settings/password
匹配修改密碼頁面的路由地址,但修改密碼頁面將沒法正常顯示。具體代碼以下:
1 import { Component } from '@angular/core'; 2 3 @Component({ 4 selector: 'settings-page', 5 template: ` 6 <div class="settings"> 7 <settings-header></settings-header> 8 <settings-sidebar></settings-sidebar> 9 <router-outlet></router-outlet> 10 </div> 11 ` 12 }) 13 export class SettingsComponent {}
另外一個頗有用的路由功能是 component-less
路由。使用 component-less
路由容許咱們將路由組合在一塊兒,並讓它們共享路由配置信息和 outlet。
例如,咱們能夠定義 setttings
路由而不須要使用 SettingsComponent
組件:
1 import { ProfileSettingsComponent } from './settings/profile/profile.component'; 2 import { PasswordSettingsComponent } from './settings/password/password.component'; 3 4 export const ROUTES: Routes = [ 5 { 6 path: 'settings', 7 children: [ 8 { path: 'profile', component: ProfileSettingsComponent }, 9 { path: 'password', component: PasswordSettingsComponent } 10 ] 11 } 12 ];
此時, /settings/profile
和 /settings/password
路由定義的內容,將顯示在 AppComponent
組件的 router-outlet
元素中。
咱們也能夠告訴路由從另外一個模塊中獲取子路由。這將咱們談論的兩個想法聯繫在一塊兒 - 咱們能夠指定另外一個模塊中定義的子路由,以及經過將這些子路由設置到特定的路徑下,來充分利用 component-less
路由的功能。
讓咱們建立一個 SettingsModule
模塊,用來保存全部 setttings
相關的路由信息:
1 import { NgModule } from '@angular/core'; 2 import { CommonModule } from '@angular/common'; 3 import { Routes, RouterModule } from '@angular/router'; 4 5 export const ROUTES: Routes = [ 6 { 7 path: '', 8 component: SettingsComponent, 9 children: [ 10 { path: 'profile', component: ProfileSettingsComponent }, 11 { path: 'password', component: PasswordSettingsComponent } 12 ] 13 } 14 ]; 15 16 @NgModule({ 17 imports: [ 18 CommonModule, 19 RouterModule.forChild(ROUTES) 20 ], 21 }) 22 export class SettingsModule {}
須要注意的是,在 SettingsModule
模塊中咱們使用 forChild()
方法,由於 SettingsModule
不是咱們應用的主模塊。
另外一個主要的區別是咱們將 SettingsModule
模塊的主路徑設置爲空路徑 ('')。由於若是咱們路徑設置爲 /settings
,它將匹配 /settings/settings
,很明顯這不是咱們想要的結果。經過指定一個空的路徑,它就會匹配 /settings
路徑,這就是咱們想要的結果。
那麼 /settings
路由信息,須要在哪裏配置?答案是在 AppModule
中。這時咱們就須要用到 loadChildren
屬性,具體以下:
export const ROUTES: Routes = [ { path: 'settings', loadChildren: './settings/settings.module#SettingsModule' } ];
須要注意的是,咱們沒有將 SettingsModule
導入到咱們的 AppModule
中,而是經過 loadChildren
屬性,告訴 Angular 路由依據 loadChildren
屬性配置的路徑去加載 SettingsModule
模塊。這就是模塊懶加載功能的具體應用,當用戶訪問 /settings/**
路徑的時候,纔會加載對應的 SettingsModule
模塊,這減小了應用啓動時加載資源的大小。
另外咱們傳遞一個字符串做爲 loadChildren
的屬性值,該字符串由三部分組成:
須要導入模塊的相對路徑
#
分隔符
導出模塊類的名稱
瞭解完路由的一些高級選項和功能,接下來咱們來介紹路由指令。
除了 router-outlet
指令,路由模塊中還提供了一些其它指令。讓咱們來看看它們如何與咱們以前介紹的內容結合使用。
爲了讓咱們連接到已設置的路由,咱們須要使用 routerLink
指令,具體示例以下:
1 <nav> 2 <a routerLink="/">Home</a> 3 <a routerLink="/settings/password">Change password</a> 4 <a routerLink="/settings/profile">Profile Settings</a> 5 </nav>
當咱們點擊以上的任意連接時,頁面不會被從新加載。反之,咱們的路徑將在 URL 地址欄中顯示,隨後進行後續視圖更新,以匹配 routerLink
中設置的值。
友情提示:咱們也能夠將 routerLink
的屬性值,改爲數組形式,以便咱們傳遞特定的路由信息
若是咱們想要連接到動態的路由地址,且該地址有一個 username
的路由變量,則咱們能夠按照如下方式配置 routerLink
對應的屬性值:
<a [routerLink]="['/profile', username]"> Go to {{ username }}'s profile. </a>
在實際開發中,咱們須要讓用戶知道哪一個路由處於激活狀態,一般狀況下咱們經過向激活的連接添加一個 class 來實現該功能。爲了解決上述問題,Angular 路由模塊爲咱們提供了 routerLinkActive
指令,該指令的使用示例以下:
<nav> <a routerLink="/settings" routerLinkActive="active">Home</a> <a routerLink="/settings/password" routerLinkActive="active">Change password</a> <a routerLink="/settings/profile" routerLinkActive="active">Profile Settings</a> </nav>
經過使用 routerLinkActive
指令,當 a
元素對應的路由處於激活狀態時,active
類將會自動添加到 a
元素上。
最後,咱們來簡單介紹一下 Router API。
咱們能夠經過路由還提供的 API 實現與 routerLink
相同的功能。要使用 Router API,咱們須要在組件類中注入 Router
對象,具體以下:
1 import { Component } from '@angular/core'; 2 import { Router } from '@angular/router'; 3 4 @Component({ 5 selector: 'app-root', 6 template: ` 7 <div class="app"> 8 <h3>Our app</h3> 9 <router-outlet></router-outlet> 10 </div> 11 ` 12 }) 13 export class AppComponent { 14 constructor(private router: Router) {} 15 }
組件類中注入的 router
對象中有一個 navigate()
方法,該方法支持的參數類型與 routerLink
指令同樣,當調用該方法後,頁面將會自動跳轉到對應的路由地址。具體使用示例以下:
1 import { Component, OnInit } from '@angular/core'; 2 import { Router } from '@angular/router'; 3 4 @Component({ 5 selector: 'app-root', 6 template: ` 7 <div class="app"> 8 <h3>Our app</h3> 9 <router-outlet></router-outlet> 10 </div> 11 ` 12 }) 13 export class AppComponent implements OnInit { 14 constructor(private router: Router) {} 15 ngOnInit() { 16 setTimeout(() => { 17 this.router.navigate(['/settings']); 18 }, 5000); 19 } 20 }
若以上代碼成功運行,用戶界面將在 5 秒後被重定向到 /settings
頁面。這個方法很是有用,例如當檢測到用戶還沒有登陸時,自動重定向到登陸頁面。
另外一個使用示例是演示頁面跳轉時如何傳遞數據,具體以下:
1 import { Component, OnInit } from '@angular/core'; 2 import { Router } from '@angular/router'; 3 4 @Component({ 5 selector: 'app-root', 6 template: ` 7 <div class="app"> 8 <h3>Users</h3> 9 <div *ngFor="let user of users"> 10 <user-component 11 [user]="user" 12 (select)="handleSelect($event)"> 13 </user-component> 14 </div> 15 <router-outlet></router-outlet> 16 </div> 17 ` 18 }) 19 export class AppComponent implements OnInit { 20 users: Username[] = [ 21 { name: 'toddmotto', id: 0 }, 22 { name: 'travisbarker', id: 1 }, 23 { name: 'tomdelonge', id: 2 } 24 ]; 25 26 constructor(private router: Router) {} 27 28 handleSelect(event) { 29 this.router.navigate(['/profile', event.name]); 30 } 31 }
Angular 路由的功能很是強大,既可使用指令方式也可使用命令式 API,但願本文能夠幫助你儘快入門,若要進一步瞭解路由詳細信息,請訪問 - Angular Router 官文文檔。
navigate()
方法外還有沒有其它方法能夠實現頁面導航?Angular Router API 爲咱們提供了 navigate()
和 navigateByUrl()
方法來實現頁面導航。那爲何會有兩個不一樣的方法呢?
使用 router.navigateByUrl()
方法與直接改變地址欄上的 URL 地址同樣,咱們使用了一個新的 URL 地址。然而 router.navigate()
方法基於一系列輸入參數,產生一個新的 URL 地址。爲了更好的區分它們之間的差別,咱們來看個例子,假設當前的 URL 地址是:
/inbox/11/message/22(popup:compose)
當咱們調用 router.navigateByUrl('/inbox/33/message/44')
方法後,此時的 URL 地址將變成 /inbox/33/message/44
。但若是咱們是調用 router.navigate('/inbox/33/message/44')
方法,當前的 URL 地址將變成 /inbox/33/message/44(popup:compose)
。