angular使用路由复用策略实现页面前进后退时是否保持原状态

问题描述:

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: any; // 当前页未进行存储时需要删除
    private static currentDelete: any; // 当前页存储过时需要删除
  
    /** 进入路由触发,判断是否是同一路由 */
    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 | null{
      // 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): any {
      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:any = [];
        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.这样就大功告成了。
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
当然可以,以下是一个简单的使用 Angular 实现分页功能的例子: 首先,安装 ngx-pagination 库,这是一个方便的 Angular 分页插件。 ``` npm install ngx-pagination --save ``` 接下来,在需要分页的组件中导入 ngx-pagination: ```typescript import { NgModule } from '@angular/core'; import { NgxPaginationModule } from 'ngx-pagination'; @NgModule({ imports: [ NgxPaginationModule ] }) ``` 在组件类中定义一个数组来存储所有要展示的数据: ```typescript export class MyComponent { data = [ { id: 1, name: 'John' }, { id: 2, name: 'Jane' }, { id: 3, name: 'Bob' }, { id: 4, name: 'Alice' }, { id: 5, name: 'Jim' }, { id: 6, name: 'Mary' }, { id: 7, name: 'Tom' }, { id: 8, name: 'Lucy' }, { id: 9, name: 'David' } ]; } ``` 在组件模板中,使用 ngx-pagination 指令来实现分页: ```html <table> <thead> <tr> <th>ID</th> <th>Name</th> </tr> </thead> <tbody> <tr *ngFor="let item of data | paginate: { itemsPerPage: 3, currentPage: p }"> <td>{{item.id}}</td> <td>{{item.name}}</td> </tr> </tbody> </table> <pagination-controls (pageChange)="p = $event"></pagination-controls> ``` 在上述模板中,我们使用了 *ngFor 指令来循环渲染每一页的数据,同使用了 paginate 指令来进行分页。其中,itemsPerPage 表示每页显示几条数据,currentPage 表示当前页码。而 pagination-controls 是 ngx-pagination 提供的组件,用来实现分页控件。 好了,这样就实现了一个简单的 Angular 分页功能
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gaiery

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值