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; } } }
完毕!!!