angular6.x系列的學習筆記記錄,仍在不斷完善中,學習地址:html
(1)組件詳解之模板語法安全
(2)組件詳解之組件通信服務器
(3)內容投影, ViewChild和ContentChildapp
(4)指令異步
(5)路由ide
通常而言,瀏覽器具備下列導航模式:svg
在地址欄輸入 URL,瀏覽器就會導航到相應的頁面。oop
在頁面中點擊連接,瀏覽器就會導航到一個新頁面。
點擊瀏覽器的前進和後退按鈕,瀏覽器就會在你的瀏覽歷史中向前或向後導航。
那麼,在angular中,是什麼決定上述的行爲呢?
對於一個新建的項目而言,只存在一個組件AppComponent,若是不增長其餘的組件,意味着全部的行爲就將在這一個組件裏面完成,這種狀況下,單一的組件將沒法保存狀態的變化,這顯然知足不了上面的需求.因此,一般狀況下,會如我在組件通信中所寫,組件之間呈以下的樹形結構:
路由就是鏈接這些組件的筋絡,它也是樹形結構的.有了它,就能夠在angular中實現上述的導航模式
能夠把路由當作是一組規則,它決定了url的變化對應着哪種狀態,具體表現就是不一樣視圖的切換
在angular中,路由是很是重要的組成部分, 組件的實例化與銷燬,模塊的加載,組件的某些生命週期鉤子的發起,都是與它有關
咱們重新建一個項目來演示一下路由的基本定義,對新建項目加以豐富,增添娛樂,學習,工做三個組件,對應的文件結構以下:
│ app.component.html
│ app.component.ts
│ app.module.ts
│
└─routes
├─happy
│ happy.component.html
│ happy.component.ts
│
├─study
│ study.component.html
│ study.component.ts
│
└─work
work.component.html
work.component.ts
如何可以實現如下的效果呢?
當前Url | 對應組件內容 |
localhost:XXX/ | HappyComponent |
localhost:XXX/work | WorkComponent |
localhost:XXX/happy | HappyComponent |
localhost:XXX/study | StudyComponent |
localhost:XXX/nothing | HappyComponent |
在AppModule中增長以下的路由配置,便可知足上面的需求
1 import { BrowserModule } from '@angular/platform-browser'; 2 import { NgModule } from '@angular/core'; 3 import { AppComponent } from './app.component'; 4 import { WorkComponent } from './routes/work/work.component'; 5 import { StudyComponent } from './routes/study/study.component'; 6 import { HappyComponent } from './routes/happy/happy.component'; 7 import { Routes, RouterModule } from '@angular/router'; 8 9 const appRoutes: Routes = [ 10 { path: '', component: HappyComponent }, 11 { path: 'work', component: WorkComponent }, 12 { path: 'happy', component: HappyComponent }, 13 { path: 'study', component: StudyComponent }, 14 { path: '**', component: HappyComponent }, 15 ] 16 17 @NgModule({ 18 declarations: [ 19 AppComponent, 20 WorkComponent, 21 StudyComponent, 22 HappyComponent 23 ], 24 imports: [ 25 RouterModule.forRoot(appRoutes), 26 BrowserModule, 27 ], 28 providers: [], 29 bootstrap: [AppComponent] 30 }) 31 32 export class AppModule { }
也許上面有一些陌生的東西,但這並不重要,讓咱們在介紹路由模塊以後一塊兒來介紹
在上述路由定義中,是把路由的配置放在模塊AppModule中進行的,這樣在簡單的配置中,是能夠接受的.
可是隨着應用的成長,會用到更多路由器特性,好比:守衛,解析器和子路由等,這時候再在模塊中進行配置,就會顯得雜亂冗腫
官方建議重構路由,將路由信息移到一個單獨的特殊用途模塊之中,叫作路由模塊。
路由模塊並非必須的,它只是優化設計的一種選擇,它的價值在配置很複雜,幷包含專門守衛和解析器服務時尤爲明顯。可以保持設計的一致性和代碼的乾淨,便於開發者查找和擴展配置.固然,在配置很簡單時,它可能看起來不少餘。
路由模塊有一系列特性:
把路由這個關注點從其它應用類關注點中分離出去。
測試特性模塊時,能夠替換或移除路由模塊。
爲路由服務提供商(包括守衛和解析器等)提供一個共同的地方。
不要聲明組件。
將上面的模塊中的路由定義轉換成路由模塊,文件結構變化以下:
│ app-routing.module.ts
│ app.component.html
│ app.component.ts
│ app.module.ts
│
└─routes
│
├─happy
│ happy.component.html
│ happy.component.ts
│
├─study
│ study.component.html
│ study.component.ts
│
└─work
work.component.html
work.component.ts
具體代碼:
1 import { BrowserModule } from '@angular/platform-browser'; 2 import { NgModule } from '@angular/core'; 3 import { AppComponent } from './app.component'; 4 import { AppRoutingModule } from './app-routing.module'; 5 import { WorkComponent } from './routes/work/work.component'; 6 import { StudyComponent } from './routes/study/study.component'; 7 import { HappyComponent } from './routes/happy/happy.component'; 8 9 10 @NgModule({ 11 declarations: [ 12 AppComponent, 13 WorkComponent, 14 StudyComponent, 15 HappyComponent 16 ], 17 imports: [ 18 BrowserModule, 19 AppRoutingModule 20 ], 21 providers: [], 22 bootstrap: [AppComponent] 23 }) 24 25 export class AppModule { }
1 import { NgModule } from '@angular/core'; 2 import { Routes, RouterModule } from '@angular/router'; 3 import { HappyComponent } from './routes/happy/happy.component'; 4 import { StudyComponent } from './routes/study/study.component'; 5 import { WorkComponent } from './routes/work/work.component'; 6 7 const routes: Routes = [ 8 { path: '', component: HappyComponent }, 9 { path: 'work', component: WorkComponent }, 10 { path: 'happy', component: HappyComponent }, 11 { path: 'study', component: StudyComponent }, 12 { path: '**', component: HappyComponent }, 13 ]; 14 15 @NgModule({ 16 imports: [RouterModule.forRoot(routes)], 17 exports: [RouterModule] 18 }) 19 export class AppRoutingModule { }
warnning
根據上面的內容,咱們有兩種配置路由的方法,即在路由模塊或者在模塊內部配置路由,但不要同時在兩處都配置。
路由器是一個調度中心,它是一套規則的列表,可以查詢當前URL對應的規則,並呈現出相應的視圖.
路由是列表裏面的一個規則,即路由定義,它有不少功能字段,上述列子中:它有一個path字段,表示該路由中的URL路徑部分和一個Component字段,表示與該路由相關聯的組件
每一個帶路由的Angular應用都有一個路由器服務的單例對象,經過路由定義的列表進行配置後使用。
上述具體的工做流程,能夠舉例簡單描述爲:
當瀏覽器地址欄的URL變化時,路徑部分/study知足了列表中path爲"study"的這個路由定義,激活對應StudyComponent的實例,顯示它的視圖
當應用程序請求導航到路徑/work時,符合了另外的規則,激活對應視圖且展現內容,並將該路徑更新到瀏覽器地址欄和歷史
Warnning
RouterModule.forRoot方法是用於註冊全應用級提供商的編碼模式.把RouterModule.forRoot()註冊到AppModule的imports中,能讓該Router服務在應用的任何地方都能使用.只在根模塊AppRoutingModule中調用RouterModule.forRoot(若是在AppModule中註冊應用的頂級路由,那就在 AppModule中調用),在其它模塊,你就必須調用RouterModule.forChild方法來註冊附屬路由.
默認路由就是上述的空路由'',表示應用的默認路徑,當URL爲空時就會訪問那裏,所以它一般會做爲起點。
最後一個路由中的**
路徑是一個通配符,當所請求的URL不匹配前面定義的路由表中的任何路徑時,路由器就會選擇此路由.這個特性可用於顯示「404 - Not Found」頁,或自動重定向到其它路由.
路由器中的路由定義列表的順序是很是重要的,它符合就近原則,當瀏覽器的URL變化時,Router會從上到下依次查找對應的Route,找到符合規則Route後,就決定顯示那個組件,那麼後面的路由就被"短路"了,例如,若是把通配符路由放在第一個,那麼不管路由怎麼變化,在他後面的路由都失去了意義,由於不論怎麼變化,他都是第一個符合規則的,因此它一般要放在最後一個
咱們配置好的路由,是在哪裏渲染內容呢?
下列是一個新建項目的app.component.html視圖內容:
1 <!--The content below is only a placeholder and can be replaced.--> 2 <div style="text-align:center"> 3 <h1> 4 Welcome to {{ title }}! 5 </h1> 6 <img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg=="> 7 </div> 8 <h2>Here are some links to help you start: </h2> 9 <ul> 10 <li> 11 <h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2> 12 </li> 13 <li> 14 <h2><a target="_blank" rel="noopener" href="https://angular.io/cli">CLI Documentation</a></h2> 15 </li> 16 <li> 17 <h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2> 18 </li> 19 </ul> 20 21 <router-outlet></router-outlet>
其中有這樣一個標籤<router-outlet></router-outlet>
RouterOutlet是一個來自路由模塊中的指令,它的用法相似於組件.它扮演一個佔位符的角色,用於在模板中標出一個位置,路由器將會把要顯示在這個出口處的組件顯示在這裏,即在宿主視圖中的RouterOutlet以後顯示組件內容.
路由和組件同樣,都是樹形結構的,能夠層層嵌套,配置子路由
在如上內容的結構下,假設在happy組件中增長text,picture,video三個組件,文件結構以下:
│ app-routing.module.ts │ app.component.html │ app.component.ts │ app.module.ts │ └─routes │ ├─happy │ │ happy-routing.module.ts │ │ happy.component.html │ │ happy.component.ts │ │ happy.module.ts │ │ │ ├─picture │ │ picture.component.html │ │ picture.component.ts │ │ │ ├─text │ │ text.component.html │ │ text.component.ts │ │ │ └─video │ video.component.html │ video.component.ts │ ├─study │ study.component.html │ study.component.ts │ └─work work.component.html work.component.ts
注意一點咱們須要在AppModule中引入HappyModule
具體代碼:
1 import { BrowserModule } from '@angular/platform-browser'; 2 import { NgModule } from '@angular/core'; 3 import { AppComponent } from './app.component'; 4 import { AppRoutingModule } from './app-routing.module'; 5 import { WorkComponent } from './routes/work/work.component'; 6 import { StudyComponent } from './routes/study/study.component'; 7 import { HappyComponent } from './routes/happy/happy.component'; 8 import { HappyModule } from './routes/happy/happy.module'; 9 10 11 @NgModule({ 12 declarations: [ 13 AppComponent, 14 WorkComponent, 15 StudyComponent, 16 HappyComponent 17 ], 18 imports: [ 19 BrowserModule, 20 HappyModule, 21 AppRoutingModule 22 ], 23 providers: [], 24 bootstrap: [AppComponent] 25 }) 26 27 export class AppModule { }
1 import { NgModule } from '@angular/core'; 2 import { Routes, RouterModule } from '@angular/router'; 3 import { HappyComponent } from './routes/happy/happy.component'; 4 import { StudyComponent } from './routes/study/study.component'; 5 import { WorkComponent } from './routes/work/work.component'; 6 7 const routes: Routes = [ 8 { path: '', component: HappyComponent }, 9 { path: 'work', component: WorkComponent }, 10 { path: 'happy', component: HappyComponent }, 11 { path: 'study', component: StudyComponent }, 12 { path: '**', component: HappyComponent }, 13 ]; 14 15 @NgModule({ 16 imports: [RouterModule.forRoot(routes)], 17 exports: [RouterModule] 18 }) 19 export class AppRoutingModule { }
1 import { NgModule } from '@angular/core'; 2 import { CommonModule } from '@angular/common'; 3 import { HappyRoutingModule } from './happy-routing.module'; 4 import { VideoComponent } from './video/video.component'; 5 import { PictureComponent } from './picture/picture.component'; 6 import { TextComponent } from './text/text.component'; 7 8 @NgModule({ 9 declarations: [ 10 VideoComponent, 11 PictureComponent, 12 TextComponent 13 ], 14 imports: [ 15 CommonModule, 16 HappyRoutingModule 17 ] 18 }) 19 export class HappyModule { }
1 import { NgModule } from '@angular/core'; 2 import { Routes, RouterModule } from '@angular/router'; 3 import { HappyComponent } from './happy.component'; 4 import { TextComponent } from './text/text.component'; 5 import { PictureComponent } from './picture/picture.component'; 6 import { VideoComponent } from './video/video.component'; 7 8 const routes: Routes = [ 9 { 10 path: 'happy', 11 component: HappyComponent, 12 children: [ 13 { 14 path: '', 15 children: [ 16 { 17 path: '', 18 component: TextComponent 19 }, 20 { 21 path: 'text', 22 component: TextComponent 23 }, 24 { 25 path: 'picture', 26 component: PictureComponent 27 }, 28 { 29 path: 'video', 30 component: VideoComponent 31 }, 32 { 33 path: '**', 34 component: TextComponent 35 } 36 ] 37 } 38 ] 39 } 40 ]; 41 42 @NgModule({ 43 imports: [RouterModule.forChild(routes)], 44 exports: [RouterModule] 45 }) 46 47 export class HappyRoutingModule { }
在子路由下面使用了一次空路由,Router支持空路徑路由,可使用它們來分組路由,而不用往 URL 中添加額外的路徑片斷
假設在路由守衛的時候,想對每個子路由進行認證,這時候就不須要一一添加,加在這個空路由上便可
在具體的應用中,咱們不可能讓全部的路由觸發都是靠路由地址的改變來實現,這是很是被動的
不少狀況下,是咱們經過事件,主動觸發路由的變化,具體內容參見組件通信,在路由跳轉中我已經寫明
目前,任何用戶都能在任什麼時候候導航到任何地方,對於大部分應用,這樣是存在安全問題的,某些用戶可能無權導航到目標組件,須要先登陸(認證)
在顯示目標組件前,可能須要先獲取某些數據。
在離開組件前,可能要先保存修改.須要詢問用戶:是否要放棄本次更改,而不用保存它們?
對於上述這些場景問題,每每須要在路由配置中添加守衛,進行處理.
守衛經過返回一個值,以控制路由器的行爲:
若是它返回 true,導航過程會繼續
若是它返回 false,導航過程就會終止,且用戶留在原地。
若是它返回 UrlTree,則取消當前的導航,而且開始導航到返回的這個 UrlTree.
warnning
守衛還能夠告訴路由器導航到別處,這樣也會取消當前的導航。要想在守衛中這麼作,就要返回 false;
守衛能夠用同步的方式返回一個布爾值,但在不少狀況下,守衛沒法用同步的方式給出答案.守衛可能會向用戶問一個問題、把更改保存到服務器,或者獲取新數據,而這些都是異步操做。所以,路由的守衛能夠返回一個Observable<boolean> Promise<boolean>,而且路由器會等待這個可觀察對象被解析爲true或false。
warnning
提供給路由器的可觀察對象還必須能結束,不然,導航就不會繼續.
路由器能夠支持多種守衛接口:
用CanActivate來處理導航到某路由的狀況。
用CanActivateChild來處理導航到某子路由的狀況。
用CanDeactivate來處理從當前路由離開的狀況.
用Resolve在路由激活以前獲取路由數據。
用CanLoad來處理異步導航到某特性模塊的狀況。
在分層路由的每一個級別上,你均可以設置多個守衛,上面提到過的空路由,在這裏會可能發揮很好的做用
路由器會先按照從最深的子路由由下往上檢查的順序來檢查CanDeactivate() 和CanActivateChild() 守衛.而後它會按照從上到下的順序檢查CanActivate()守衛. 若是特性模塊是異步加載的,在加載它以前還會檢查CanLoad()守衛. 若是任何一個守衛返回 false,其它還沒有完成的守衛會被取消,這樣整個導航就被取消.
下面以路由認證和處理未保存的更改來認識一下路由守衛
增添一個登錄的功能,在訪問/work的時候須要登錄才能訪問,不然跳轉到登錄頁面
首先添加一個auth的服務,來保存登陸狀態和登錄與註銷的功能
ng generate service auth/auth (簡寫ng g s auth/auth)
再添加一個auth守衛
ng generate guard auth/auth (簡寫ng g g auth/auth)
最後在對應路由中添加這個守衛
1 import { Injectable } from '@angular/core'; 2 import { CanActivate, CanLoad, Route, UrlSegment, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router'; 3 import { Observable } from 'rxjs'; 4 import { AuthService } from './auth.service'; 5 6 @Injectable({ 7 providedIn: 'root' 8 }) 9 export class AuthGuard implements CanActivate { 10 constructor( 11 private authService: AuthService, 12 private router: Router 13 ) { } 14 15 canActivate( 16 next: ActivatedRouteSnapshot, 17 18 state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { 19 20 let url = state.url 21 22 return this.checkLogin(url); 23 } 24 25 checkLogin(url: string) { 26 if (this.authService.isLoggedIn) return true; 27 28 this.authService.redirectUrl = url; 29 30 this.router.navigate(["login"]); 31 32 return false; 33 } 34 }
1 import { Injectable } from '@angular/core'; 2 import { Observable, of } from 'rxjs'; 3 import { tap, delay } from 'rxjs/operators'; 4 5 @Injectable({ 6 providedIn: 'root' 7 }) 8 export class AuthService { 9 //是否登錄的狀態 10 isLoggedIn: boolean = false; 11 12 // 登陸後重定向的地址 13 redirectUrl: string = ''; 14 15 constructor() { } 16 17 login(): Observable<boolean> { 18 return of(true).pipe( 19 delay(1000), 20 tap(val => this.isLoggedIn = true) 21 ); 22 } 23 24 logout(): void { 25 this.isLoggedIn = false; 26 } 27 }
1 { path: 'work', canActivate: [AuthGuard], component: WorkComponent },
在某些頁面,咱們可能會處理相似表單之類的提交數據的操做,有時候在沒有完成數據提交以前,咱們就有意或無心的離開了當前頁面,好比:不當心觸發了瀏覽器後退事件,或者主動的點擊某個鏈接跳出當前頁面,放棄這次操做.咱們不能把每一次的路由變化都視爲有意爲之,若是真是不當心跳轉的,那麼可能填寫的不少數據都付諸流水了
所以,當用戶導航在頁面以外時,應該彈出一個面板,詢問是否離開當前頁面, 若是用戶選擇了取消,就留在當前頁面,並容許更多改動.若是用戶選擇了確認,那就放棄這次操做
首先,添加一個服務,用來彈出面板,確認用戶的操做.
ng generate service dialog(ng g s dialog)
爲DialogService 添加一個confirm()方法,以提醒用戶確認.window.confirm是一個阻塞型操做,它會顯示一個模態對話框,並等待用戶的交互。
1 import { Injectable } from '@angular/core'; 2 import { of, Observable } from 'rxjs'; 3 4 @Injectable({ 5 providedIn: 'root' 6 }) 7 export class DialogService { 8 9 confirm(meesage: string): Observable<boolean> { 10 11 const confirmation = window.confirm(meesage || '確認離開嗎?') 12 13 return of(confirmation); 14 } 15 }
而後,生成一個守衛guard,以檢查組件(任意組件都可)中是否存在canDeactivate()方法。
ng generate guard can-deactivate(ng g g can-deactivate)
對於這個任意組件,守衛只須要檢查它是否有一個canDeactivate()方法,並調用它,這就讓該守衛能夠複用.
假設用來保存上述StudyComponent組件裏面的內容,那麼須要修改爲以下內容:
1 import { Component, OnInit } from '@angular/core'; 2 import { Observable } from 'rxjs'; 3 import { DialogService } from '../../dialog/dialog.service'; 4 import { CanComponentDeactivate } from '../../can-deactivate/can-deactivate.guard'; 5 6 7 @Component({ 8 selector: 'app-study', 9 templateUrl: './study.component.html' 10 }) 11 export class StudyComponent implements OnInit, CanComponentDeactivate { 12 isChange: boolean = true;//提交數據是否發生變化 13 constructor( 14 public dialogService: DialogService 15 ) { } 16 17 ngOnInit() { } 18 19 canDeactivate(): Observable<boolean> | boolean { 20 if (!this.isChange) return true; //未發生變化 21 return this.dialogService.confirm('是否離開當前頁面?'); 22 } 23 }
這是通用的狀況下,固然也能夠爲組件建立特定的CanDeactivate守衛,那就把守衛換成一下內容便可
1 import { Injectable } from '@angular/core'; 2 import { CanDeactivate } from '@angular/router'; 3 import { StudyComponent } from '../routes/study/study.component'; 4 5 6 @Injectable({ 7 providedIn: 'root', 8 }) 9 export class CanDeactivateGuard implements CanDeactivate<StudyComponent> { 10 canDeactivate(component: StudyComponent) { 11 return component.canDeactivate ? component.canDeactivate() : true; 12 } 13 }
固然組件就不須要implements那個通用的接口CanComponentDeactivate了
最後把這個守衛放在對應的路由定義下就好了
隨着應用程序的不斷壯大,程序的加載時間將會過長,這是咱們不得不正視的一個嚴重問題.
如何才能解決這個問題呢?最好的辦法就是引進異步路由:能夠得到在請求時才惰性加載特性模塊的能力. 惰性加載有多個優勢:
你能夠只在用戶請求時才加載某些特性區。
對於那些只訪問應用程序某些區域的用戶,這樣能加快加載速度。
你能夠持續擴充惰性加載特性區的功能,而不用增長初始加載的包體積。
惰性加載是加載的模塊,因此須要對上述的結構改進一下:
│ app-routing.module.ts │ app.component.html │ app.component.ts │ app.module.ts │ └─routes │ routes-routing.module.ts │ routes.module.ts │ ├─happy │ │ happy-routing.module.ts │ │ happy.component.html │ │ happy.component.ts │ │ happy.module.ts │ │ │ ├─picture │ │ picture.component.html │ │ picture.component.ts │ │ │ ├─text │ │ text.component.html │ │ text.component.ts │ │ │ └─video │ video.component.html │ video.component.ts │ ├─study │ study-routing.module.ts │ study.component.html │ study.component.ts │ study.module.ts │ └─work work-routing.module.ts work.component.html work.component.ts work.module.ts
具體代碼:
1 import { NgModule } from '@angular/core'; 2 import { Routes, RouterModule } from '@angular/router'; 3 4 const routes: Routes = []; 5 6 @NgModule({ 7 imports: [RouterModule.forRoot(routes)], 8 exports: [RouterModule] 9 }) 10 11 export class AppRoutingModule { }
1 import { BrowserModule } from '@angular/platform-browser'; 2 import { NgModule } from '@angular/core'; 3 import { AppComponent } from './app.component'; 4 import { AppRoutingModule } from './app-routing.module'; 5 import { RoutesModule } from './routes/routes.module'; 6 7 8 @NgModule({ 9 declarations: [ 10 AppComponent 11 ], 12 imports: [ 13 BrowserModule, 14 RoutesModule, 15 AppRoutingModule 16 ], 17 providers: [], 18 bootstrap: [AppComponent] 19 }) 20 21 export class AppModule { }
1 import { NgModule } from '@angular/core'; 2 import { Routes, RouterModule } from '@angular/router'; 3 import { HappyComponent } from './happy/happy.component'; 4 5 const routes: Routes = [ 6 { path: '', component: HappyComponent }, 7 { 8 path: 'work', 9 loadChildren: './work/work.module#WorkModule' 10 }, 11 { 12 path: 'study', 13 loadChildren: './study/study.module#StudyModule' 14 }, 15 { path: '**', component: HappyComponent }, 16 ] 17 @NgModule({ 18 imports: [RouterModule.forRoot(routes)], 19 exports: [RouterModule] 20 }) 21 export class RoutesRoutingModule { }
1 import { NgModule } from '@angular/core'; 2 import { CommonModule } from '@angular/common'; 3 import { RoutesRoutingModule } from './routes-routing.module'; 4 import { HappyModule } from './happy/happy.module'; 5 6 @NgModule({ 7 declarations: [], 8 imports: [ 9 CommonModule, 10 HappyModule, 11 RoutesRoutingModule 12 ] 13 }) 14 export class RoutesModule { }
1 import { NgModule } from '@angular/core'; 2 import { Routes, RouterModule } from '@angular/router'; 3 import { WorkComponent } from './work.component'; 4 5 const routes: Routes = [ 6 { path: '', redirectTo: 'work', pathMatch: 'full' }, 7 { path: 'work', component: WorkComponent } 8 ]; 9 10 @NgModule({ 11 imports: [RouterModule.forChild(routes)], 12 exports: [RouterModule] 13 }) 14 export class WorkRoutingModule { }
1 import { NgModule } from '@angular/core'; 2 import { CommonModule } from '@angular/common'; 3 4 import { WorkRoutingModule } from './work-routing.module'; 5 import { WorkComponent } from './work.component'; 6 7 @NgModule({ 8 declarations: [WorkComponent], 9 imports: [ 10 CommonModule, 11 WorkRoutingModule 12 ] 13 }) 14 export class WorkModule { }
1 import { NgModule } from '@angular/core'; 2 import { Routes, RouterModule } from '@angular/router'; 3 import { HappyComponent } from './happy.component'; 4 import { TextComponent } from './text/text.component'; 5 import { PictureComponent } from './picture/picture.component'; 6 import { VideoComponent } from './video/video.component'; 7 8 const routes: Routes = [ 9 { 10 path: 'happy', 11 component: HappyComponent, 12 children: [ 13 { 14 path: '', 15 children: [ 16 { 17 path: '', 18 component: TextComponent 19 }, 20 { 21 path: 'text', 22 component: TextComponent 23 }, 24 { 25 path: 'picture', 26 component: PictureComponent 27 }, 28 { 29 path: 'video', 30 component: VideoComponent 31 }, 32 { 33 path: '**', 34 component: TextComponent 35 } 36 ] 37 } 38 ] 39 } 40 ]; 41 42 @NgModule({ 43 imports: [RouterModule.forChild(routes)], 44 exports: [RouterModule] 45 }) 46 47 export class HappyRoutingModule { }
1 import { NgModule } from '@angular/core'; 2 import { CommonModule } from '@angular/common'; 3 import { HappyRoutingModule } from './happy-routing.module'; 4 import { VideoComponent } from './video/video.component'; 5 import { PictureComponent } from './picture/picture.component'; 6 import { TextComponent } from './text/text.component'; 7 import { HappyComponent } from './happy.component'; 8 9 @NgModule({ 10 declarations: [ 11 HappyComponent, 12 VideoComponent, 13 PictureComponent, 14 TextComponent 15 ], 16 imports: [ 17 CommonModule, 18 HappyRoutingModule 19 ] 20 }) 21 export class HappyModule { }
HappyModule是啓動後的默認路由模塊,須要在啓動時加載,全部沒有惰性加載
對於WorkModule和StudyModule,只有在咱們訪問的時候,它們纔會加載,這樣就節約了啓動時的加載時間
惰性加載和從新配置工做只會發生一次,也就是在該路由首次被請求時.在後續的請求中,該模塊和路由都是當即可用的。
在上面路由認證那節,咱們對/work地址進行了認證,它會阻止未登錄用戶訪問/work,若是用戶未登陸,它就會跳轉到登陸頁。
若是是以模塊的形式 那麼路由器仍然會加載WorkModule—— 即便用戶沒法訪問它的任何一個組件.理想的方式是,只有在用戶已登陸的狀況下才加載WorkModule。
使用CanLoad守衛,它只在用戶已登陸而且/work的時候,才加載 WorkModule一次。
現有的auth.guard.ts 的checkLogin()方法中已經有了支持CanLoad守衛的基礎邏輯。
打開auth.guard.ts ,從@angular/router 中導入CanLoad接口,把它添加到AuthGuard類的implements列表中.而後實現canLoad,代碼以下:
1 import { Injectable } from '@angular/core'; 2 import { CanActivate, CanLoad, Route, UrlSegment, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router'; 3 import { Observable } from 'rxjs'; 4 import { AuthService } from './auth.service'; 5 6 @Injectable({ 7 providedIn: 'root' 8 }) 9 export class AuthGuard implements CanActivate,CanLoad { 10 constructor( 11 private authService: AuthService, 12 private router: Router 13 ) { } 14 15 canActivate( 16 next: ActivatedRouteSnapshot, 17 18 state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { 19 20 let url = state.url 21 22 return this.checkLogin(url); 23 } 24 25 canLoad(route: Route): boolean { 26 let url = `/${route.path}`; 27 return this.checkLogin(url); 28 } 29 30 checkLogin(url: string) { 31 if (this.authService.isLoggedIn) return true; 32 33 this.authService.redirectUrl = url; 34 35 this.router.navigate(["login"]); 36 37 return false; 38 } 39 }
路由器會把canLoad()方法的route參數設置爲準備訪問的目標URL.若是用戶已經登陸了,checkLogin()方法就會重定向到那個 URL。
路由的內容是實在過於豐富,本文只能詳盡部份內容,如想了解更多內容,請參見官方文檔
(終)
文檔信息
感謝您的閱讀,若是您以爲閱讀本文對您有幫助,請點一下「推薦」按鈕。本文歡迎各位轉載,可是轉載文章以後必須在文章頁面中給出做者和原文鏈接。
can-deactivate