1、定义路由快照

  新建文件SimpleReuseStrategy.ts

import { RouteReuseStrategy, DefaultUrlSerializer, ActivatedRouteSnapshot, DetachedRouteHandle, Route } from '@angular/router';

export class SimpleReuseStrategy implements RouteReuseStrategy {
static _cacheRouters: { [key: string]: any } = {};
private static _donotWriteKey: string = '' //用来避免关掉自定义tab重新写入 /**是否允许复用路由 */
shouldDetach(route: ActivatedRouteSnapshot): boolean {
if (!route.routeConfig || route.routeConfig.loadChildren) {
return false;
}
let key = this.getPathNotParams(route);
switch (key) {
case '/master/pripowerdc0240/home': return true;
case '/master/pripowerdc0240/list': return true;
case '/master/pripowerdc0240/detail/:id': return true;
default: return false;
}
} /**当路由离开时会触发 按path作为key存储路由快照&组件当前实例对象*/
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
let key = this.getCompleteKey(route);
if (SimpleReuseStrategy._donotWriteKey != '' && SimpleReuseStrategy._donotWriteKey == this.getCompleteKey(route)) {
console.log('不存储快照' + key)
SimpleReuseStrategy._donotWriteKey = ''
return;
}
SimpleReuseStrategy._cacheRouters[key] = {
snapshot: route,
handle: handle
};
} /**是否允许还原路由 */
shouldAttach(route: ActivatedRouteSnapshot): boolean {
// 在缓存中有的都认为允许还原路由
let key = this.getCompleteKey(route);
return !!route.routeConfig && !!SimpleReuseStrategy._cacheRouters[key];
} /**从缓存中获取快照 */
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
// 从缓存中获取快照,若无则返回null
let key = this.getCompleteKey(route);
if (!route.routeConfig || !SimpleReuseStrategy._cacheRouters[key]) return null;
return SimpleReuseStrategy._cacheRouters[key].handle;
} /** 进入路由触发,判断是否同一路由 */
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
// 同一路由时复用路由
return this.getCompleteKey(future) === this.getCompleteKey(curr);
} public static clearCacheRoutersBy(key: string, donotWrite = false) {
delete SimpleReuseStrategy._cacheRouters[key];
if (donotWrite) {
//不跳转写入
SimpleReuseStrategy._donotWriteKey = key;
}
} private getCompleteKey(route: ActivatedRouteSnapshot): string {
let keys: string[] = []
route.pathFromRoot.map(v =>
v.url.map(segment => {
keys.push(segment.toString());
})
);
return '/' + keys.join('/');
} private getPathNotParams(route: ActivatedRouteSnapshot): string {
let key = '';
route.pathFromRoot.map(x => {
if (x.routeConfig?.path) {
key += '/' + x.routeConfig?.path;
}
});
return key;
} }

  在app.module.ts添加providers

  providers: [ { provide: RouteReuseStrategy, useClass: SimpleReuseStrategy }],

  在需要缓存快照的子路由添加providers,如

import { NgModule } from '@angular/core';
import { Routes, RouterModule, RouteReuseStrategy } from '@angular/router';
import { HomeComponent } from './home/home.component'
import { DetailComponent } from './detail/detail.component'
import { ListComponent } from './list/list.component'
import { SimpleReuseStrategy } from 'src/app/SimpleReuseStrategy'; const routes: Routes = [
{
path: 'home',
component: HomeComponent,
data: { title: '首页' }
},
{
path: 'detail/:id',
component: DetailComponent,
data: { title: '详情' }
},
{
path: 'list',
component: ListComponent,
data: { title: '列表' }
}
]; @NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
providers: [
{ provide: RouteReuseStrategy, useClass: SimpleReuseStrategy }
],
})
export class Pripowerdc0240RoutingModule { }

2、制作tab

  在母版页定义tab,先上截图

  

  ts

import { Component, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { of, pipe } from 'rxjs';
import { filter, map, mergeMap } from 'rxjs/operators';
import { FoodNode, TreeService } from 'src/app/services/common/tree.service';
import { SimpleReuseStrategy } from 'src/app/SimpleReuseStrategy'; @Component({
selector: 'app-master',
templateUrl: './master.component.html',
styleUrls: ['./master.component.scss']
})
export class MasterComponent implements OnInit { show = true;
@ViewChild('asideleft') asideleft: any;
@ViewChild('asideright') asideright: any; menuList: CusRouterTabInfo[] = [];
selectedIndex = 0;
constructor(
public treeService: TreeService,
private router: Router,
private activatedRoute: ActivatedRoute
) {
this.iniMenu();
} ngOnInit(): void {
this.treeService.dataSource.data = this.TREE_DATA;
} iniMenu() {
this.router.events.pipe(
filter(event => {
return event instanceof NavigationEnd
}),
map(event => {
let path = (event as NavigationEnd).url
return { route: this.activatedRoute, path: path }
}),
map(model => {
while (model.route.firstChild) model.route = model.route.firstChild;
return model;
}),
filter(model => model.route.outlet === 'primary'),
).subscribe(model => {
model.route.data.subscribe(data => {
if(!data['title']){
return;//没有配置title的默认不添加到menuList
}
let hasIndex = this.menuList.findIndex(x => x.path == model.path);
if (hasIndex != -1) {//menuList已存在
this.selectedIndex = hasIndex;
return;
}
let menu: CusRouterTabInfo = { title: data['title'], path: model.path, isSelect: true };
this.menuList.push(menu);
this.selectedIndex = this.menuList.findIndex(x => x.path == model.path)
})
})
} showleft() {
this.asideleft.nativeElement.style.transform = 'translate(0, 0)';
this.asideright.nativeElement.style.transform = 'translate(0, 0)';
this.asideright.nativeElement.style.width = 'calc(100% - 12vw)';
this.asideright.nativeElement.style.marginleft = '12vw';
this.show = true;
}
hideleft() {
this.asideleft.nativeElement.style.transform = 'translate(-100%, 0)';
this.asideright.nativeElement.style.transform = 'translate(-12vw, 0)';
this.asideright.nativeElement.style.width = '100%';
this.asideright.nativeElement.style.marginleft = 0;
this.show = false;
} TREE_DATA: FoodNode[] = [
{
id: '0120',
name: 'UPS电源设备',
children: [
{ id: '/manager_specialist/partsearch/list', name: '80-NET系列' },
]
},
{
id: '0240',
name: '一次电源配电柜',
children: [
{ id: '/master/pripowerdc0240/home', name: 'home' },
{ id: '/master/pripowerdc0240/detail/111', name: 'detail' },
{ id: '/master/pripowerdc0240/detail/222', name: 'detail' },
{ id: '/master/pripowerdc0240/list', name: 'list' },
],
}
] deleteMenuList(path: string) {
if (this.menuList.length <= 1) { //只有一个不删
return;
}
let delIndex = this.menuList.findIndex(x => x.path == path);
this.menuList.splice(delIndex, 1);//删除
SimpleReuseStrategy.clearCacheRoutersBy(path, true);
if (delIndex == 0) { //关的是第一个
this.router.navigate([this.menuList[0].path]);
return;
}
//关的其他
this.router.navigate([this.menuList[delIndex - 1].path]);
}
gotoPath(path: string) {
console.log('gotoPath:' + path);
this.router.navigate([path]);
}
} interface CusRouterTabInfo {
title: string
path: string
isSelect: boolean
}

  html

<div class="left" #asideleft>
<div class="logobox">
<div class="logo">
<img src="/assets/images/vlogo.png">
</div>
</div> <div class="tree">
<mat-tree [dataSource]="treeService.dataSource" [treeControl]="treeService.treeControl">
<mat-tree-node *matTreeNodeDef="let node" matTreeNodePadding>
<button mat-icon-button disabled></button>
<span [routerLink]="node.id" routerLinkActive="active">&nbsp;&nbsp;{{node.name}}</span>
<!-- <span routerLink="/plm/productconfig/list/{{node.id}}" routerLinkActive="active">{{node.name}}</span> -->
</mat-tree-node>
<mat-tree-node *matTreeNodeDef="let node;when: treeService.hasChild" matTreeNodeToggle>
<button mat-icon-button [attr.aria-label]="'Toggle ' + node.name">
<mat-icon class="mat-icon-rtl-mirror">
{{treeService.treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}
</mat-icon>
</button>
<span>{{node.name}}</span>
</mat-tree-node>
</mat-tree>
</div> </div> <div class="content" #asideright>
<div class="contenttop">
<div class="contenttopleft">
<div (click)="showleft()" *ngIf="!show">
<mat-icon>reorder</mat-icon>
</div>
<div (click)="hideleft()" *ngIf="show">
<mat-icon>reorder</mat-icon>
</div>
</div>
<div class="contenttopcenter">
<b>专家库后台管理</b>
</div>
<div class="contenttopright">
Dear:<b style="color: #fff;">admin</b>
<button mat-button style="color: #fff;margin-right: 2vw;">退出<mat-icon>logout</mat-icon></button>
</div>
</div> <div class="router">
<mat-tab-group mat-align-tabs="start" [selectedIndex]="selectedIndex" *ngIf="menuList.length!=0">
<mat-tab *ngFor="let menu of menuList" class="mattab">
<div style="width: 100%">
<ng-template mat-tab-label>
<input type="text" [value]="menu.title" readonly='true' (click)="gotoPath(menu.path)">
<mat-icon class="closeIcon" (click)="deleteMenuList(menu.path)">close</mat-icon>
</ng-template>
</div>
</mat-tab>
</mat-tab-group>
<!-- <button mat-button (click)="loadCacheRouters()">load</button> -->
</div> <div class="contentrouting">
<router-outlet></router-outlet>
</div>
</div>

  scss

.left{
float: left;
background-color: #607B8B;
height: 100vh;
width: 12vw;
transition: all 1s;
z-index: 2;
position:absolute;
.logobox{
background-color: #68838B;
border-radius: 7px;
width: 100%;
height: 4vw;
.logo{
padding: 1vh 1vw;
img{
width: 100%;
height: 100%;
}
}
}
} .content{
z-index: 1;
position:absolute;
margin-left: 12vw;
background-color: #DCDCDC;
height: 100vh;
width: 88vw;
transition: all 1s;
// transition: all 1s;
} .contenttop{
height: 6vh;
width: 100%;
line-height: 5.5vh;
background-color: #B5B5B5;
position: relative;
.contenttopleft{
float: left;
line-height: 100%;
.mat-icon{
cursor: pointer;
}
}
.contenttopcenter{
float: left;
// width: 30vw;
margin-left: 35vw;
b{
font-size: 1.3rem;
color: #DCDCDC;
}
}
.contenttopright{
float: right;
}
} .contentrouting{
height: calc(94vh - 30px);
width: 100%;
line-height: unset;
} ::ng-deep{
.mat-tree{
background:none;
mat-tree-node{
padding-left: 0 !important;
}
.mat-tree-node{
color: #DCDCDC;
// color: red;
min-height: 20px;
height: auto;
padding-left: 10px;
button{
width: 20px;
height: 30px;
line-height: 30px;
}
span{
cursor: pointer;
width: 100%;
height: auto;
user-select: none;
}
}
}
} .router{
input{
border: 0;
// background: none;
cursor: pointer;
height: 28px;
width: 100%;
}
input:focus{
outline: none;
}
.closeIcon{
margin-left: 2px;
font-size: 12px;
height: 100%;
background: red;
border-radius: 50%;
width: 12px;
height: 12px;
position: absolute;
right: 5px;
} } .active{
color: #CD950C;
} :host ::ng-deep{
.mat-tab-label{
height: 30px !important;
border-top-left-radius: 15px;
border-top-right-radius: 15px;
.mat-tab-label-content{
position: absolute;
left: 0;
}
}
.mat-tab-group{
height: 30px !important;
.mat-tab-labels{
height: 30px !important;
}
}
}

  完毕!!!

Angular 利用路由快照实现tab的更多相关文章

  1. angular利用ui-router登录检查

    angular利用ui-router登录检查 SAP都会有这个问题,session过期或者页面被刷新的情况下应该进入登录页. 监听ui-router的satte事件可以实现当state切换的时候检查登 ...

  2. 烂泥:KVM利用LVM快照快速部署虚拟机

    本文由秀依林枫提供友情赞助,首发于烂泥行天下. 上一篇文章介绍了有关KVM利用LVM快照备份和恢复虚拟机的功能,这篇文章我们来介绍,如何利用LVM快照功能为KVM快速部署虚拟机. 部署虚拟机需要以下几 ...

  3. 烂泥:LVM学习之KVM利用LVM快照备份与恢复虚拟机

    本文由秀依林枫提供友情赞助,首发于烂泥行天下. 最近一段时间一直在学习有关LVM逻辑卷方面的知识,前几篇文章介绍了有关LVM的逻辑卷的基本相关知识,包括逻辑卷及卷组的扩容与缩小.今天我们再来介绍LVM ...

  4. 利用路由修改thinkphp框架开发的后台地址

    一般我们写前台是home 后台是 admin那么我们的后台是 域名/admin 那我们要随时修改这个地址是非常麻烦的事情开始可能会想到,就是把模块名称改掉,或者分组(3.1版)这样的话不但要改配置,连 ...

  5. Angular 4 路由介绍

    Angular 4 路由 1. 创建工程 ng new router --routing 2. 创建home和product组件 ng g component home ng g component ...

  6. angular -- ng-ui-route路由及其传递参数?script标签版

    考虑到 多视图等因素,所以 angular 的路由考虑使用 ng-ui-route来做,而不使用 ng-route来做! <!DOCTYPE html> <html lang=&qu ...

  7. angular 之路由

    1.用angular-cli建一个工程自带路由怎么做? 命令:ng new  项目名 --routing 2.怎么使用路由器和路由器的一些基本使用. //html页面 <a routerLink ...

  8. angular 前端路由不生效解决方案

    angular 前端路由不生效解决方案 Intro 最近使用 Angular 为我的活动室预约项目开发一个前后端分离的客户端,在部署上遇到了一个问题,前端路由不生效,这里记录一下.本地开发正常,但是部 ...

  9. Angular配置路由以及动态路由取值传值跳转

    Angular配置路由 1.找到 app-routing.module.ts 配置路由 引入组件 import { HomeComponent } from './home/home.componen ...

  10. 理解 angular 的路由功能

    相信很多人使用angular 都是因为他路由功能而用的 深入理解ANGULARUI路由_UI-ROUTER 最近在用 ionic写个webapp 看到几个demo中路由有好几种,搞的有点晕,查下资料研 ...

随机推荐

  1. 彻底理解Python中的闭包和装饰器(上)

    什么是闭包 闭包(Closure)其实并不是Python独有的特性,很多语言都有对闭包的支持.(当然,因为Python是笔者除C/C++之外学习的第二门语言,所以也是第一次遇到闭包.)简而言之,闭包实 ...

  2. PAM8403 3.3V音频功放调试笔记

    做I2S输出用了PT8211(实际上买到的丝印是GH8211), 双声道, LSB格式, 工作正常但是输出功率非常低, 喇叭声音要贴近了才能勉强听到, 所以打算做一个PT8211带功放的I2S模块. ...

  3. 编程思想的转变 软件开发目录规范 collections、time、datetime、 random模块

    目录 编程思想的转变 软件目录开发规范(重要) 内置模块:collections模块 简介 具名元组 namedtuple 实现二维坐标系 实现扑克牌 队列与堆栈 collection.deque() ...

  4. PHP7.2 装mongodb 遇到的坑,完美解决!

    公司要做QA安全测试,组长就丢了一个源码包给我,什么资料都无. 系统是个Laravel框架,源码都是从线上git下来.然后看了本地composer.json 没有生成vendor 第一步安装 comp ...

  5. 盘点现在用的SqlServer 5种分页方式和拉姆达表达式分页,进来看看吧。

    现在基本上大家都在使用各种轮子自带的分页,大家是否还记得sql分页怎么写? 今天我们就来盘一盘怎么写和用哪种方式写. 欢迎大家评论区讨论. 1.ROW_NUMBER() OVER()方式(SQL201 ...

  6. [Linux Kernel 源码分析] 通过vconfig配置vlan的系统调用/驱动流程分析

    By YuCloud (蓝天上的云℡ - 博客园 https://www.cnblogs.com/yucloud/) 转载请注明出处 vconfig源码分析 vlan/vconfig.c at mas ...

  7. PRIx64:uint64_t类型输出为十六进制格式

    #include <stdio.h> #include <stdint.h> #include <inttypes.h> int main(void) { uint ...

  8. ssm——spring整理

    目录 1.概述 2.Spring工厂与IOC 2.1.为什么要有Spring框架 2.2.什么是IOC 2.Spring工厂对实例注入 2.1.使用标签进行注入 2.2.使用注解进行注入 2.2.3. ...

  9. python之路51 聚合查询 分组查询

    图书管理系统 1.表设计 先考虑普通字段再考虑外键字段 数据库迁移.测试数据录入 2.首页展示 3.书籍展示 4.书籍添加 5.书籍编辑 后端如何获取用户想要编辑的数据.前端如何展示出待编辑的数据 6 ...

  10. while2.c程序

    /*while2.c程序->注意分號的位置*/ 1 #include<stdio.h> 2 int main(void) 3 { 4 int n=0; 5 6 while(n++&l ...