详解Angular路由之路由守卫


Posted in Javascript onMay 10, 2018

一、路由守卫

当用户满足一定条件才被允许进入或者离开一个路由。

路由守卫场景:

只有当用户登录并拥有某些权限的时候才能进入某些路由。

一个由多个表单组成的向导,例如注册流程,用户只有在当前路由的组件中填写了满足要求的信息才可以导航到下一个路由。

当用户未执行保存操作而试图离开当前导航时提醒用户。

Angular提供了一些钩子帮助控制进入或离开路由。这些钩子就是路由守卫,可以通过这些钩子实现上面场景。

  1. CanActivate: 处理导航到某路由的情况。
  2. CanDeactivate: 处理从当前路由离开的情况。
  3. Resolve: 在路由激活之前获取路由数据。

配置路由时候用到一些属性,path, component, outlet, children, 路由守卫也是路由属性。

二、CanActivate

实例:只让登录用户进入产品信息路由。

新建guard目录。目录下新建login.guard.ts。

LoginGuard类实现CanActivate接口,返回true或false,Angular根据返回值判断请求通过或不通过。

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,在指定路由守卫。

canActivate可以指定多个守卫,值是一个数组。

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 }
];

效果:点商品详情链接控制台会提醒用户未登录,不能进入商品详情路由。

详解Angular路由之路由守卫

三、CanDeactivate

离开时候的路由守卫。提醒用户执行保存操作后才能离开。

在guard目录下新建一个unsave.guard.ts的文件。

CanDeactivate接口有一个范型,指定当前组件的类型。

CanDeactivate方法第一个参数就是接口指定的范型类型的组件,根据这个要保护的组件的状态,或者调用方法来决定用户是否能够离开。

import { CanDeactivate } from "@angular/router";
import { ProductComponent } from "../product/product.component";

export class UnsaveGuard implements CanDeactivate<ProductComponent>{
  //第一个参数 范型类型的组件
  //根据当前要保护组件 的状态 判断当前用户是否能够离开
  canDeactivate(component: ProductComponent){
    return window.confirm('你还没有保存,确定要离开吗?');
  }
}

配置路由,同样先加到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 { }

效果:

点ok离开当前页面,cancel留在当前页面。

详解Angular路由之路由守卫

四、Resolve守卫

http请求数据返回有延迟,导致模版无法立刻显示。

数据返回之前模版上所有需要用插值表达式显示某个controller的值的地方都是空的。用户体验不好。

resolve解决办法:在进入路由之前去服务器读数据,把需要的数据都读好以后,带着这些数据进到路由里,立刻就把数据显示出来。

实例:

在进入商品信息路由之前,准备好商品信息再进入路由。 拿不到信息,或者拿信息出问题了,直接跳到错误信息页面,或者弹出提示,就不再进入目标路由。

先在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类实现了Resolve接口。

Resolve也要声明一个范型,范型就是resolve要解析出来的数据的类型。

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导航回首页
      this.router.navigate(["/home"]);
      return undefined;
    }
  }
}

路由配置:Provider里声明,product路由里配置。

resolve是一个对象,对象里参数的名字就是想传入的参数的名字product,用ProductResolve来解析生成。

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是一个对象
   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>

效果:

点商品详情链接,传入商品ID为2,在resolve守卫中是正确id,会返回一条商品数据。

点商品详情按钮,传入商品ID是3,是错误id,会直接跳转到主页。

详解Angular路由之路由守卫

详解Angular路由之路由守卫

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
获取当前网页document.url location.href区别总结
May 10 Javascript
JavaScript Sort 表格排序
Oct 31 Javascript
深入理解JavaScript系列(2) 揭秘命名函数表达式
Jan 15 Javascript
js实现window.open不被拦截的解决方法汇总
Oct 30 Javascript
基于JavaScript实现TAB标签效果
Jan 12 Javascript
纯JS实现弹性导航条效果
Mar 06 Javascript
小发现之浅谈location.search与location.hash的问题
Jun 23 Javascript
vue.js或js实现中文A-Z排序的方法
Mar 08 Javascript
微信小程序实现单列下拉菜单效果
Apr 25 Javascript
ligerUI的ligerDialog关闭刷新的方法
Sep 27 Javascript
JavaScript实现无限轮播效果
Nov 19 Javascript
如何用JavaScipt测网速
May 09 Javascript
JavaScript实现一个简易的计算器实例代码
May 10 #Javascript
浅谈node.js 命令行工具(cli)
May 10 #Javascript
Js经典案例的实例代码
May 10 #Javascript
Vue使用vux-ui自定义表单验证遇到的问题及解决方法
May 10 #Javascript
vuex与组件联合使用的方法
May 10 #Javascript
vue项目中公用footer组件底部位置的适配问题
May 10 #Javascript
解决vue 按钮多次点击重复提交数据问题
May 10 #Javascript
You might like
星际争霸 Starcraft 编年史
2020/03/14 星际争霸
php环境套包 dedeampz 伪静态设置示例
2014/03/26 PHP
CodeIgniter CLI模式简介
2014/06/17 PHP
php打包压缩文件之ZipArchive方法用法分析
2016/04/30 PHP
thinkPHP商城公告功能开发问题分析
2016/12/01 PHP
php显示页码分页类的封装
2017/06/08 PHP
window.addeventjs事件驱动函数集合addEvent等
2008/02/19 Javascript
实现超用户体验 table排序javascript实现代码
2009/06/22 Javascript
Js setInterval与setTimeout(定时执行与循环执行)的代码(可以传入参数)
2010/06/11 Javascript
cument.execCommand()用法深入理解
2012/12/04 Javascript
JavaScript解析URL参数示例代码
2013/08/12 Javascript
JS实现网页滚动条感应鼠标变色的方法
2015/02/26 Javascript
js绘制圆形和矩形的方法
2015/08/05 Javascript
跟我学习javascript的call(),apply(),bind()与回调
2015/11/16 Javascript
深入理解$.each和$(selector).each
2016/05/15 Javascript
JS button按钮实现submit按钮提交效果
2016/11/01 Javascript
微信小程序 地图(map)实例详解
2016/11/16 Javascript
详解JS中的快速排序与冒泡
2017/01/10 Javascript
AngularJS $http模块POST请求实现
2017/04/08 Javascript
JS实现线性表的链式表示方法示例【经典数据结构】
2017/04/11 Javascript
手机注册发送验证码倒计时的简单实例
2017/11/15 Javascript
浅谈 Vue 项目优化的方法
2017/12/16 Javascript
vue 自定义指令自动获取文本框焦点的方法
2018/08/25 Javascript
VUE2.0 ElementUI2.0表格el-table自适应高度的实现方法
2018/11/28 Javascript
Python利用ansible分发处理任务
2015/08/04 Python
Android 兼容性问题:java.lang.UnsupportedOperationException解决办法
2017/03/19 Python
利用python库在局域网内传输文件的方法
2018/06/04 Python
python实现电脑自动关机
2018/06/20 Python
Python使用修饰器进行异常日志记录操作示例
2019/03/19 Python
兰蔻美国官网:Lancome美国
2017/04/25 全球购物
好邻里事迹材料
2014/01/16 职场文书
开工庆典邀请函范文
2014/01/16 职场文书
国际金融专业自荐信
2014/07/05 职场文书
MySQL InnoDB ReplicaSet(副本集)简单介绍
2021/04/24 MySQL
详解vue身份认证管理和租户管理
2021/05/25 Vue.js
MySQL中TIMESTAMP类型返回日期时间数据中带有T的解决
2022/12/24 MySQL