詳解Angular路由之路由守衛(wèi)
一、路由守衛(wèi)
當(dāng)用戶滿足一定條件才被允許進(jìn)入或者離開一個(gè)路由。
路由守衛(wèi)場(chǎng)景:
只有當(dāng)用戶登錄并擁有某些權(quán)限的時(shí)候才能進(jìn)入某些路由。
一個(gè)由多個(gè)表單組成的向?qū)?,例如注?cè)流程,用戶只有在當(dāng)前路由的組件中填寫了滿足要求的信息才可以導(dǎo)航到下一個(gè)路由。
當(dāng)用戶未執(zhí)行保存操作而試圖離開當(dāng)前導(dǎo)航時(shí)提醒用戶。
Angular提供了一些鉤子幫助控制進(jìn)入或離開路由。這些鉤子就是路由守衛(wèi),可以通過(guò)這些鉤子實(shí)現(xiàn)上面場(chǎng)景。
- CanActivate: 處理導(dǎo)航到某路由的情況。
- CanDeactivate: 處理從當(dāng)前路由離開的情況。
- Resolve: 在路由激活之前獲取路由數(shù)據(jù)。
配置路由時(shí)候用到一些屬性,path, component, outlet, children, 路由守衛(wèi)也是路由屬性。
二、CanActivate
實(shí)例:只讓登錄用戶進(jìn)入產(chǎn)品信息路由。
新建guard目錄。目錄下新建login.guard.ts。
LoginGuard類實(shí)現(xiàn)CanActivate接口,返回true或false,Angular根據(jù)返回值判斷請(qǐng)求通過(guò)或不通過(guò)。
import { CanActivate } from "@angular/router";
export class LoginGuard implements CanActivate{
canActivate(){
let loggedIn :boolean= Math.random()<0.5;
if(!loggedIn){
console.log("用戶未登錄");
}
return loggedIn;
}
}
配置product路由。先把LoginGuard加入providers,在指定路由守衛(wèi)。
canActivate可以指定多個(gè)守衛(wèi),值是一個(gè)數(shù)組。
const routes: Routes = [
{ path: '', redirectTo : 'home',pathMatch:'full' },
{ path: 'chat', component: ChatComponent, outlet: "aux"},//輔助路由
{ path: 'home', component: HomeComponent },
{ path: 'product/:id', component: ProductComponent, children:[
{ path: '', component : ProductDescComponent },
{ path: 'seller/:id', component : SellerInfoComponent }
] ,canActivate: [LoginGuard]},
{ path: '**', component: Code404Component }
];
效果:點(diǎn)商品詳情鏈接控制臺(tái)會(huì)提醒用戶未登錄,不能進(jìn)入商品詳情路由。

三、CanDeactivate
離開時(shí)候的路由守衛(wèi)。提醒用戶執(zhí)行保存操作后才能離開。
在guard目錄下新建一個(gè)unsave.guard.ts的文件。
CanDeactivate接口有一個(gè)范型,指定當(dāng)前組件的類型。
CanDeactivate方法第一個(gè)參數(shù)就是接口指定的范型類型的組件,根據(jù)這個(gè)要保護(hù)的組件的狀態(tài),或者調(diào)用方法來(lái)決定用戶是否能夠離開。
import { CanDeactivate } from "@angular/router";
import { ProductComponent } from "../product/product.component";
export class UnsaveGuard implements CanDeactivate<ProductComponent>{
//第一個(gè)參數(shù) 范型類型的組件
//根據(jù)當(dāng)前要保護(hù)組件 的狀態(tài) 判斷當(dāng)前用戶是否能夠離開
canDeactivate(component: ProductComponent){
return window.confirm('你還沒(méi)有保存,確定要離開嗎?');
}
}
配置路由,同樣先加到provider,再配置路由。
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { ProductComponent } from './product/product.component';
import { Code404Component } from './code404/code404.component';
import { ProductDescComponent } from './product-desc/product-desc.component';
import { SellerInfoComponent } from './seller-info/seller-info.component';
import { ChatComponent } from './chat/chat.component';
import { LoginGuard } from './guard/login.guard';
import { UnsaveGuard } from './guard/unsave.guard';
const routes: Routes = [
{ path: '', redirectTo : 'home',pathMatch:'full' },
{ path: 'chat', component: ChatComponent, outlet: "aux"},//輔助路由
{ path: 'home', component: HomeComponent },
{ path: 'product/:id', component: ProductComponent, children:[
{ path: '', component : ProductDescComponent },
{ path: 'seller/:id', component : SellerInfoComponent }
] ,canActivate: [LoginGuard],
canDeactivate: [UnsaveGuard]},
{ path: '**', component: Code404Component }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers: [LoginGuard,UnsaveGuard]
})
export class AppRoutingModule { }
效果:
點(diǎn)ok離開當(dāng)前頁(yè)面,cancel留在當(dāng)前頁(yè)面。

四、Resolve守衛(wèi)
http請(qǐng)求數(shù)據(jù)返回有延遲,導(dǎo)致模版無(wú)法立刻顯示。
數(shù)據(jù)返回之前模版上所有需要用插值表達(dá)式顯示某個(gè)controller的值的地方都是空的。用戶體驗(yàn)不好。
resolve解決辦法:在進(jìn)入路由之前去服務(wù)器讀數(shù)據(jù),把需要的數(shù)據(jù)都讀好以后,帶著這些數(shù)據(jù)進(jìn)到路由里,立刻就把數(shù)據(jù)顯示出來(lái)。
實(shí)例:
在進(jìn)入商品信息路由之前,準(zhǔn)備好商品信息再進(jìn)入路由。 拿不到信息,或者拿信息出問(wèn)題了,直接跳到錯(cuò)誤信息頁(yè)面,或者彈出提示,就不再進(jìn)入目標(biāo)路由。
先在product.component.ts中聲明商品信息類型。
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { ProductComponent } from './product/product.component';
import { Code404Component } from './code404/code404.component';
import { ProductDescComponent } from './product-desc/product-desc.component';
import { SellerInfoComponent } from './seller-info/seller-info.component';
import { ChatComponent } from './chat/chat.component';
import { LoginGuard } from './guard/login.guard';
import { UnsaveGuard } from './guard/unsave.guard';
const routes: Routes = [
{ path: '', redirectTo : 'home',pathMatch:'full' },
{ path: 'chat', component: ChatComponent, outlet: "aux"},//輔助路由
{ path: 'home', component: HomeComponent },
{ path: 'product/:id', component: ProductComponent, children:[
{ path: '', component : ProductDescComponent },
{ path: 'seller/:id', component : SellerInfoComponent }
] ,canActivate: [LoginGuard],
canDeactivate: [UnsaveGuard]},
{ path: '**', component: Code404Component }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers: [LoginGuard,UnsaveGuard]
})
export class AppRoutingModule { }
在guard目錄下新建product.resolve.ts。ProductResolve類實(shí)現(xiàn)了Resolve接口。
Resolve也要聲明一個(gè)范型,范型就是resolve要解析出來(lái)的數(shù)據(jù)的類型。
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from "@angular/router";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs/Observable";
import { Product } from "../product/product.component";
@Injectable()
export class ProductResolve implements Resolve<Product>{
constructor(private router: Router) {
}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any {
let productId: number = route.params["id"];
if (productId == 2) { //正確id
return new Product(1, "iPhone7");
} else { //id不是1導(dǎo)航回首頁(yè)
this.router.navigate(["/home"]);
return undefined;
}
}
}
路由配置:Provider里聲明,product路由里配置。
resolve是一個(gè)對(duì)象,對(duì)象里參數(shù)的名字就是想傳入的參數(shù)的名字product,用ProductResolve來(lái)解析生成。
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { ProductComponent } from './product/product.component';
import { Code404Component } from './code404/code404.component';
import { ProductDescComponent } from './product-desc/product-desc.component';
import { SellerInfoComponent } from './seller-info/seller-info.component';
import { ChatComponent } from './chat/chat.component';
import { LoginGuard } from './guard/login.guard';
import { UnsaveGuard } from './guard/unsave.guard';
import { ProductResolve } from './guard/product.resolve';
const routes: Routes = [
{ path: '', redirectTo : 'home',pathMatch:'full' },
{ path: 'chat', component: ChatComponent, outlet: "aux"},//輔助路由
{ path: 'home', component: HomeComponent },
{ path: 'product/:id', component: ProductComponent, children:[
{ path: '', component : ProductDescComponent },
{ path: 'seller/:id', component : SellerInfoComponent }
] ,
// canActivate: [LoginGuard],
// canDeactivate: [UnsaveGuard],
resolve:{ //resolve是一個(gè)對(duì)象
product : ProductResolve //想傳入product,product由ProductResolve生成
}},
{ path: '**', component: Code404Component }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers: [LoginGuard,UnsaveGuard,ProductResolve]
})
export class AppRoutingModule { }
修改一下product.component.ts 和模版,顯示商品id和name。
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
@Component({
selector: 'app-product',
templateUrl: './product.component.html',
styleUrls: ['./product.component.css']
})
export class ProductComponent implements OnInit {
private productId: number;
private productName: string;
constructor(private routeInfo: ActivatedRoute) { }
ngOnInit() {
// this.routeInfo.params.subscribe((params: Params)=> this.productId=params["id"]);
this.routeInfo.data.subscribe(
(data:{product:Product})=>{
this.productId=data.product.id;
this.productName=data.product.name;
}
);
}
}
export class Product{
constructor(public id:number, public name:string){
}
}
<div class="product">
<p>
這里是商品信息組件
</p>
<p>
商品id是: {{productId}}
</p>
<p>
商品名稱是: {{productName}}
</p>
<a [routerLink]="['./']">商品描述</a>
<a [routerLink]="['./seller',99]">銷售員信息</a>
<router-outlet></router-outlet>
</div>
效果:
點(diǎn)商品詳情鏈接,傳入商品ID為2,在resolve守衛(wèi)中是正確id,會(huì)返回一條商品數(shù)據(jù)。
點(diǎn)商品詳情按鈕,傳入商品ID是3,是錯(cuò)誤id,會(huì)直接跳轉(zhuǎn)到主頁(yè)。


以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
angular+ionic返回上一頁(yè)并刷新頁(yè)面
這篇文章主要介紹了angular+ionic返回上一頁(yè)并刷新頁(yè)面,需要的朋友可以參考下2017-08-08
Angular使用過(guò)濾器uppercase/lowercase實(shí)現(xiàn)字母大小寫轉(zhuǎn)換功能示例
這篇文章主要介紹了Angular使用過(guò)濾器uppercase/lowercase實(shí)現(xiàn)字母大小寫轉(zhuǎn)換功能,涉及AngularJS過(guò)濾器針對(duì)字符串轉(zhuǎn)換的簡(jiǎn)單使用技巧,需要的朋友可以參考下2018-03-03
AngularJS實(shí)踐之使用NgModelController進(jìn)行數(shù)據(jù)綁定
大家都知道AngularJS中的指令是其尤為復(fù)雜的一個(gè)部分,但是這也是其比較好玩的地方。這篇文章我們就來(lái)說(shuō)一說(shuō)如何在我們自定義的指令中,利用ngModel的controller來(lái)做雙向數(shù)據(jù)綁定,本文對(duì)大家學(xué)習(xí)AngularJS具有一定的參考借鑒價(jià)值,有需要的朋友們可以參考借鑒。2016-10-10
對(duì)angularJs中controller控制器scope父子集作用域的實(shí)例講解
今天小編就為大家分享一篇對(duì)angularJs中controller控制器scope父子集作用域的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-10-10
Angular 4.x中表單Reactive Forms詳解
這篇文章主要介紹了Angular 4.x中表單Reactive Forms的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),相信對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-04-04
AngularJS 與Bootstrap實(shí)現(xiàn)表格分頁(yè)實(shí)例代碼
這篇文章主要介紹了AngularJS 與Bootstrap實(shí)現(xiàn)表格分頁(yè)的相關(guān)資料,并附實(shí)例代碼和實(shí)現(xiàn)效果圖,需要的朋友可以參考下2016-10-10
使用AngularJS 跨站請(qǐng)求如何解決jsonp請(qǐng)求問(wèn)題
這篇文章主要介紹了使用AngularJS 跨站請(qǐng)求如何解決jsonp請(qǐng)求問(wèn)題,下面通過(guò)本文給大家分享解決辦法,需要的朋友參考下2017-01-01

