Angular 利用路由快照实现tab
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"> {{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; } } }
完毕!!!

浙公网安备 33010602011771号