问题描述:
angular单页面应用,有列表和编辑两个页面。列表页包含多个查询条件及分页,选中一条数据进行编辑,路由更新至编辑页,编辑完成后返回列表页,此时,客户希望列表页保持离开时的状态不变,只单独更新这一条数据,其他情况下列表页正常初始化。
解决方案:
对列表页使用路由复用策略,根据上一个页面是否是编辑页来判断是否进行初始化。如果是编辑页跳转过来,此时列表页不应该执行ngOninit函数,且对单条数据进行更新,如果不是,那列表页执行ngOninit,正常初始化。
上代码:
1.新建route-strategy.service.ts
import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router'; import { Injectable } from '@angular/core'; interface IRouteConfigData { reuse: boolean; } interface ICachedRoute { handle: DetachedRouteHandle; data: IRouteConfigData; } @Injectable() export class ZwRouteReuseStrategy implements RouteReuseStrategy { private static routeCache = new Map<string, ICachedRoute>(); private static waitDelete: string; // 当前页未进行存储时需要删除 private static currentDelete: string; // 当前页存储过时需要删除 /** 进入路由触发,判断是否是同一路由 */ shouldReuseRoute( future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot ): boolean { const IsReturn = future.routeConfig === curr.routeConfig && JSON.stringify(future.params) == JSON.stringify(curr.params); return IsReturn; } /** 表示对所有路由允许复用 如果你有路由不想利用可以在这加一些业务逻辑判断,这里判断是否有data数据判断是否复用 */ shouldDetach(route: ActivatedRouteSnapshot): boolean { if (this.getRouteData(route)) { return true; } return false; } /** 当路由离开时会触发。按path作为key存储路由快照&组件当前实例对象 */ store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void { // const url = this.getFullRouteUrl(route); const url = this.getRouteUrl(route); const data = this.getRouteData(route); if ( ZwRouteReuseStrategy.waitDelete && ZwRouteReuseStrategy.waitDelete === url ) { // 如果待删除是当前路由,且未存储过则不存储快照 ZwRouteReuseStrategy.waitDelete = null; return null; } else { // 如果待删除是当前路由,且存储过则不存储快照 if ( ZwRouteReuseStrategy.currentDelete && ZwRouteReuseStrategy.currentDelete === url ) { ZwRouteReuseStrategy.currentDelete = null; return null; } else { if (handle) { ZwRouteReuseStrategy.routeCache.set(url, { handle, data }); this.addRedirectsRecursively(route); } } } } /** 若 path 在缓存中有的都认为允许还原路由 */ shouldAttach(route: ActivatedRouteSnapshot): boolean { // const url = this.getFullRouteUrl(route); const url = this.getRouteUrl(route); const handle = ZwRouteReuseStrategy.routeCache.has(url) ? ZwRouteReuseStrategy.routeCache.get(url).handle : null; const data = this.getRouteData(route); const IsReturn = data && ZwRouteReuseStrategy.routeCache.has(url) && handle != null; return IsReturn; } /** 从缓存中获取快照,若无则返回nul */ retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle { const url = this.getRouteUrl(route); const data = this.getRouteData(route); const IsReturn = data && ZwRouteReuseStrategy.routeCache.has(url) ? ZwRouteReuseStrategy.routeCache.get(url).handle : null; return IsReturn; } private addRedirectsRecursively(route: ActivatedRouteSnapshot): void { const config = route.routeConfig; if (config) { if (!config.loadChildren) { const routeFirstChild = route.firstChild; const routeFirstChildUrl = routeFirstChild ? this.getRouteUrlPaths(routeFirstChild).join('/') : ''; const childConfigs = config.children; if (childConfigs) { const childConfigWithRedirect = childConfigs.find( c => c.path === '' && !!c.redirectTo ); if (childConfigWithRedirect) { childConfigWithRedirect.redirectTo = routeFirstChildUrl; } } } route.children.forEach(childRoute => this.addRedirectsRecursively(childRoute) ); } } private getRouteUrl(route: ActivatedRouteSnapshot) { return ( route['_routerState'].url.replace(/\//g, '_') + '_' + (route.routeConfig.loadChildren || route.routeConfig.component .toString() .split('(')[0] .split(' ')[1]) ); } private getFullRouteUrl(route: ActivatedRouteSnapshot): string { return this.getFullRouteUrlPaths(route) .filter(Boolean) .join('/') .replace('/', '_'); } private getFullRouteUrlPaths(route: ActivatedRouteSnapshot): string[] { const paths = this.getRouteUrlPaths(route); return route.parent ? [...this.getFullRouteUrlPaths(route.parent), ...paths] : paths; } private getRouteUrlPaths(route: ActivatedRouteSnapshot): string[] { return route.url.map(urlSegment => urlSegment.path); } private getRouteData(route: ActivatedRouteSnapshot): IRouteConfigData { return ( route.routeConfig && (route.routeConfig.data as IRouteConfigData) && route.routeConfig.data.reuse ); } /** 用于删除路由快照*/ public deleteRouteSnapshot(url: string): void { if (url[0] === '/') { url = url.substring(1); } url = url.replace('/', '_'); if (ZwRouteReuseStrategy.routeCache.has(url)) { ZwRouteReuseStrategy.routeCache.delete(url); ZwRouteReuseStrategy.currentDelete = url; } else { ZwRouteReuseStrategy.waitDelete = url; } } public clear() { ZwRouteReuseStrategy.routeCache.clear(); } public clearExcept(list) { if (!list || !ZwRouteReuseStrategy.routeCache) return; try { let waitDelete = []; ZwRouteReuseStrategy.routeCache.forEach((value: ICachedRoute, key) => { let handle: any = value.handle; let url = handle.route.value._routerState.snapshot.url; if (list.indexOf(url) < 0) { waitDelete.push(key); } }); waitDelete.forEach(item => { ZwRouteReuseStrategy.routeCache.delete(item); }); } catch (error) { console.log('clearExcept error', error); } } }
2.app.module.ts中引入
providers:[{ provide: RouteReuseStrategy, useClass: ZwRouteReuseStrategy }]
3.配置路由,各个模按需配置路由复用策略
在xxx.routing.module.ts文件中,在需要路由复用的模块路由上加上data: { reuse:true },这样该模块就不会默认执行ngOninit函数了。
const routes: Routes = [ { path: '', redirectTo: 'list' }, { path: "list", component: ListComponent, data: { reuse:true } }, { path: "detail/:id", component: DetailComponent} ];
4.模块内部逻辑list.component.ts
constructor(private router:Router) { this.router.events.pipe(filter(event => event instanceof NavigationStart)).subscribe((event: NavigationStart) => { if(event.url==='/app/xxx/list'){ // 防止在其他模块时这里也执行,也就是在自己模块处理该问题即可 // 当前是列表页,且来源页是编辑页时,不刷新页面 if (this.router.url.indexOf("/app/xxx/detail")!==-1) { const idArr = this.router.url.split("/"); const id = idArr[idArr.length-1];// 获取编辑的那条数据的id this.updateSingle(id);//单条更新 } else if (this.router.url.indexOf("/app/xxx/detail")==-1) { // 不是编辑页跳转过来的,都走ngOninit函数 this.ngOnInit(); } } }); } ngOnInit(): void{ // 根据各个筛选条件及页码查询列表 this.searchList(); } searchList(){ //获取列表数据 } updateSingle(id){ // 单条更新数据 }
5.这样就大功告成了。
本文转载自: gaiery,版权归原作者所有,本博客仅以学习目的的传播渠道,不作版权和内容观点阐述,转载时根据场景需要有所改动。
最新评论