Angular使用路由复用实现单页多窗(Tab)

我们在开发后台管理系统时,一个很重要的需求就是多窗口编辑,来回切换使用。一种思路是使用iframe来记录多个多页面展示,本文通过Angular路由复用来实现这一需求。

路由复用

对于绝大部分应用来说,在切换到某一个路由后,组件初始化后展示,切换到另一个路由后,该组件及其状态和路由状态一并删除,在下一次进入时会重新初始化保证数据不会出错,但在开发后台管理系统时,往往需要多个路由页面来回切换,这时候就需要引入路由复用——RouteReuseStrategy

常用方法

Angular 的路由复用主要需要关注的几个方法。

策略方法:

shouldDetach 是否允许路由被复用

shouldAttach 是否允许还原路由

shouldReuseRoute 进入路由判断为同一路由时是否复用路由

使用方法:

store 当路由切换时会触发,缓存上一路由状态

retrieve 获取缓存的路由

下面请看代码

import { RouteReuseStrategy, DefaultUrlSerializer, ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router';
import { IdentityService } from './services/identity.service';

export class AppRouteReuseStrategy implements RouteReuseStrategy {
    public static handlers: { key: string, value: DetachedRouteHandle }[] = [];
    public static close = '';

    /** 表示对所有路由允许复用 如果你有路由不想利用可以在这加一些业务逻辑判断 */
    public shouldDetach(route: ActivatedRouteSnapshot): boolean {
        if (route?.routeConfig?.path === 'path') {
            return false;
        }
        if (route.firstChild) {
            // console.log(route?.routeConfig?.path);
            return false;
        }
        return true;
    }

    /** 当路由离开时会触发。按path作为key存储路由快照&组件当前实例对象 */
    public store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
        while (route.firstChild) { route = route.firstChild; }
        if (route.routeConfig !== null) {
            if (!!route.routeConfig.path) {
                if (route.children.length === 0) {
                    let current = '';
                    route.pathFromRoot.forEach(x => {
                        if (!!x.routeConfig.path!) {
                            current += `/${x?.routeConfig?.path}`;
                        }
                    });
                    // console.log(!!AppRouteReuseStrategy.handlers.filter(x => x.key === route.data.module)[0]);
                    if (!(!!AppRouteReuseStrategy.handlers.filter(x => x.key === route.data.module)[0])) {

                        if (!!AppRouteReuseStrategy.close && AppRouteReuseStrategy.close === route.data.module) {
                            return;
                        }
                        AppRouteReuseStrategy.handlers.push({ key: route.data.module, value: handle });

                    }
                }
            }
        }
    }

    /** 若 path 在缓存中有的都认为允许还原路由 */
    public shouldAttach(route: ActivatedRouteSnapshot): boolean {
        if (!!route.routeConfig) {
            if (!!route.routeConfig.path) {
                if (route.children.length === 0) {
                    let current = '';
                    route.pathFromRoot.forEach(x => {
                        if (x?.routeConfig?.path !== undefined && x?.routeConfig?.path !== null) {
                            current += `/${x?.routeConfig?.path}`;
                        }
                    });
                    return !!route.routeConfig && !!AppRouteReuseStrategy.handlers.filter(x => x.key === route.data.module)[0];
                }
                return false;
            }
            else {
                return false;
            }
        }
        else { return false; }
    }

    /** 从缓存中获取快照,若无则返回nul */
    public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
        if (!route.routeConfig) {
            return null;
        }
        if (!!route.routeConfig) {
            if (!!route.routeConfig.path) {
                if (route.children.length === 0) {
                    let current = '';
                    route.pathFromRoot.forEach(x => {
                        if (x?.routeConfig?.path !== undefined && x?.routeConfig?.path !== null) {
                            current += `/${x?.routeConfig?.path}`;
                        }
                    });
                    const handle = AppRouteReuseStrategy.handlers.filter(x => x.key === route.data.module)[0];
                    return !!handle ? handle.value : null;
                }
            }
        }
        return null;
    }

    /** 进入路由触发,判断是否同一路由 */
    public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
        return future.routeConfig === curr.routeConfig;
    }
}

同时,在AppModule中的provider将路由复用策略注册进去。

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

注意: 本文实现的方法是针对不同路由来进行复用,如果需要对同一路由对不同参数进行复用还需有更深入的考虑。

发布时间:2021-05-16
其他阅读

在ASP.NET Core中使用过滤器

ASP.NET Core是一个高性能的Web开发框架,过滤器(Filter)在ASP.NET Core中被用于路由方法之上,可以在路由方法执行前后完成一些额外的操作。本文将会介绍在ASP.NET Core中使用 IActionFilter 实现过滤器并读取路由方法中的参数。

查看原文

Angular使用路由复用实现单页多窗(Tab)

我们在开发后台管理系统时,一个很重要的需求就是多窗口编辑,来回切换使用。一种思路是使用iframe来记录多个多页面展示,本文通过Angular路由复用来实现这一需求。

查看原文

解决sqlite依赖无法打包单文件的问题

在一次WPF开发中,选用了sqlite作为内嵌数据库,使用 ystem.Data.SQLite 库来调用,在使用 Fody 进行单文件打包时,发现打包文成后会出现 x86 和 x64 两个特定的文件夹,分别对应着32位和64位的 SQLite.Interop.dll,本文介绍修改项目文件来实现将 sqlite 通信库一起打包成单文件的方法。

查看原文

自驾环游抚仙湖

抚仙湖作为云南第三大湖,以前只去过北边几个开发比较好的区域,周末闲来无事,准备自驾完整地环湖一圈,从呈贡出发,沿着国道开到澄江,然后环湖一圈,经过江川后再到澄江,吃完饭后回到呈贡。

查看原文

管道技术——中间件的灵魂

在现代Web开发中,中间件技术使用越来越广泛,本文带大家了解中间件的基础,同时也是中间件的灵魂所在,管道技术。在C#中,依赖于委托,我们可以很容易就实现一个中间件管道。所以在阅读本文前,请确保你已经学会了什么是委托,包括但不限于Delegate,Action,Func。除此之外,本文还会使用到反射相关知识,请确保你已经学会了什么是反射。

查看原文