gpt4 book ai didi

javascript - 如何为 Angular 2 中的特定路由实现 RouteReuseStrategy shouldDetach

转载 作者:IT王子 更新时间:2023-10-29 02:46:02 25 4
gpt4 key购买 nike

我有一个 Angular 2 模块,我在其中实现了路由,并希望在导航时存储状态。
用户应该能够:

  • 使用“搜索公式”搜索文档
  • 导航到结果之一
  • 导航回“搜索结果” - 不与服务器通信

  • 这是可能的,包括 RouteReuseStrategy .
    问题是:
    如何实现不应存储文档?
    所以应该存储路由路径“documents”的状态,而不应该存储路由路径“documents/:id”的状态?

    最佳答案

    嘿安德斯,好问题!

    我有和你几乎一样的用例,并且想做同样的事情!用户搜索 > 获取结果 > 用户导航到结果 > 用户返回 > 繁荣 快速返回结果,但您不想存储用户导航到的特定结果。

    tl;博士

    您需要有一个实现 RouteReuseStrategy 的类并在 ngModule 中提供您的策略.如果要修改路由存储时,修改shouldDetach功能。当它返回时 true , Angular 存储路由。如果要在附加路由时修改,修改shouldAttach功能。当shouldAttach返回 true,Angular 将使用存储的路由代替请求的路由。这是一个 Plunker供你玩耍。

    关于RouteReuseStrategy

    通过问这个问题,您已经了解 RouteReuseStrategy 允许您告诉 Angular 不要销毁组件,而实际上是保存它以供以后重新渲染。这很酷,因为它允许:

  • 减少服务器调用
  • 提速
  • 并且组件在默认情况下呈现在与它相同的状态

  • 如果您想暂时离开页面,即使用户输入了 ,最后一个也很重要。拍品 的文本。由于表单数量过多,企业应用程序会喜欢此功能!

    这就是我想出的解决问题的方法。正如你所说,你需要利用 RouteReuseStrategy由 @angular/router 在 3.4.1 及更高版本中提供。

    待办事项

    第一 确保您的项目具有 @angular/router 版本 3.4.1 或更高版本。

    下一个 , 创建一个文件,该文件将容纳实现 RouteReuseStrategy 的类.我调用我的 reuse-strategy.ts并将其放入 /app用于保管的文件夹。现在,这个类应该是这样的:

    import { RouteReuseStrategy } from '@angular/router';

    export class CustomReuseStrategy implements RouteReuseStrategy {
    }

    (不要担心您的 TypeScript 错误,我们将解决所有问题)

    完成基础工作 通过将类(class)提供给您的 app.module .请注意,您还没有写 CustomReuseStrategy ,但应该继续和 import它来自 reuse-strategy.ts都一样。还有 import { RouteReuseStrategy } from '@angular/router';
    @NgModule({
    [...],
    providers: [
    {provide: RouteReuseStrategy, useClass: CustomReuseStrategy}
    ]
    )}
    export class AppModule {
    }

    最后一块 正在编写控制路由是否分离、存储、检索和重新附加的类。在我们讨论旧的复制/粘贴之前,我将在这里对机制做一个简短的解释,据我所知。对于我描述的方法,请引用下面的代码,当然还有大量文档 在代码中 .
  • 导航时,shouldReuseRoute火灾。这个对我来说有点奇怪,但如果它返回 true ,然后它实际上会重用您当前所在的路线,并且不会触发任何其他方法。如果用户正在导航,我只会返回 false。
  • shouldReuseRoute返回 false , shouldDetach火灾。 shouldDetach确定是否要存储路由,并返回 boolean表示一样多。 这是您应该决定存储/不存储路径的地方 ,我会通过检查要存储的路径数组来实现 route.routeConfig.path , 如果 path 返回 false不存在于数组中。
  • shouldDetach返回 true , store被解雇,这是您存储您想要的有关路线的任何信息的机会。无论您做什么,您都需要存储 DetachedRouteHandle因为这是 Angular 稍后用来识别您存储的组件的内容。下面,我存储了 DetachedRouteHandleActivatedRouteSnapshot进入我类(class)的本地变量。

  • 所以,我们已经看到了存储的逻辑,但是导航到一个组件呢? Angular 如何决定拦截你的导航并将存储的导航放在它的位置?
  • 再次,在 shouldReuseRoute 之后已返回 false , shouldAttach运行,这是您确定是否要重新生成或使用内存中的组件的机会。如果要重用已存储的组件,请返回 true一切顺利!
  • 现在 Angular 会问你,“你想让我们使用哪个组件?”,你将通过返回该组件的 DetachedRouteHandle 来指明这一点。来自 retrieve .

  • 这几乎就是您需要的所有逻辑!在 reuse-strategy.ts 的代码中,下面,我还给你留下了一个漂亮的函数,可以比较两个对象。我用它来比较 future 路线的 route.paramsroute.queryParams与存储的。如果这些都匹配,我想使用存储的组件而不是生成一个新的组件。但是你怎么做是 由你决定!

    重用策略.ts

    /**
    * reuse-strategy.ts
    * by corbfon 1/6/17
    */

    import { ActivatedRouteSnapshot, RouteReuseStrategy, DetachedRouteHandle } from '@angular/router';

    /** Interface for object which can store both:
    * An ActivatedRouteSnapshot, which is useful for determining whether or not you should attach a route (see this.shouldAttach)
    * A DetachedRouteHandle, which is offered up by this.retrieve, in the case that you do want to attach the stored route
    */
    interface RouteStorageObject {
    snapshot: ActivatedRouteSnapshot;
    handle: DetachedRouteHandle;
    }

    export class CustomReuseStrategy implements RouteReuseStrategy {

    /**
    * Object which will store RouteStorageObjects indexed by keys
    * The keys will all be a path (as in route.routeConfig.path)
    * This allows us to see if we've got a route stored for the requested path
    */
    storedRoutes: { [key: string]: RouteStorageObject } = {};

    /**
    * Decides when the route should be stored
    * If the route should be stored, I believe the boolean is indicating to a controller whether or not to fire this.store
    * _When_ it is called though does not particularly matter, just know that this determines whether or not we store the route
    * An idea of what to do here: check the route.routeConfig.path to see if it is a path you would like to store
    * @param route This is, at least as I understand it, the route that the user is currently on, and we would like to know if we want to store it
    * @returns boolean indicating that we want to (true) or do not want to (false) store that route
    */
    shouldDetach(route: ActivatedRouteSnapshot): boolean {
    let detach: boolean = true;
    console.log("detaching", route, "return: ", detach);
    return detach;
    }

    /**
    * Constructs object of type `RouteStorageObject` to store, and then stores it for later attachment
    * @param route This is stored for later comparison to requested routes, see `this.shouldAttach`
    * @param handle Later to be retrieved by this.retrieve, and offered up to whatever controller is using this class
    */
    store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    let storedRoute: RouteStorageObject = {
    snapshot: route,
    handle: handle
    };

    console.log( "store:", storedRoute, "into: ", this.storedRoutes );
    // routes are stored by path - the key is the path name, and the handle is stored under it so that you can only ever have one object stored for a single path
    this.storedRoutes[route.routeConfig.path] = storedRoute;
    }

    /**
    * Determines whether or not there is a stored route and, if there is, whether or not it should be rendered in place of requested route
    * @param route The route the user requested
    * @returns boolean indicating whether or not to render the stored route
    */
    shouldAttach(route: ActivatedRouteSnapshot): boolean {

    // this will be true if the route has been stored before
    let canAttach: boolean = !!route.routeConfig && !!this.storedRoutes[route.routeConfig.path];

    // this decides whether the route already stored should be rendered in place of the requested route, and is the return value
    // at this point we already know that the paths match because the storedResults key is the route.routeConfig.path
    // so, if the route.params and route.queryParams also match, then we should reuse the component
    if (canAttach) {
    let willAttach: boolean = true;
    console.log("param comparison:");
    console.log(this.compareObjects(route.params, this.storedRoutes[route.routeConfig.path].snapshot.params));
    console.log("query param comparison");
    console.log(this.compareObjects(route.queryParams, this.storedRoutes[route.routeConfig.path].snapshot.queryParams));

    let paramsMatch: boolean = this.compareObjects(route.params, this.storedRoutes[route.routeConfig.path].snapshot.params);
    let queryParamsMatch: boolean = this.compareObjects(route.queryParams, this.storedRoutes[route.routeConfig.path].snapshot.queryParams);

    console.log("deciding to attach...", route, "does it match?", this.storedRoutes[route.routeConfig.path].snapshot, "return: ", paramsMatch && queryParamsMatch);
    return paramsMatch && queryParamsMatch;
    } else {
    return false;
    }
    }

    /**
    * Finds the locally stored instance of the requested route, if it exists, and returns it
    * @param route New route the user has requested
    * @returns DetachedRouteHandle object which can be used to render the component
    */
    retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {

    // return null if the path does not have a routerConfig OR if there is no stored route for that routerConfig
    if (!route.routeConfig || !this.storedRoutes[route.routeConfig.path]) return null;
    console.log("retrieving", "return: ", this.storedRoutes[route.routeConfig.path]);

    /** returns handle when the route.routeConfig.path is already stored */
    return this.storedRoutes[route.routeConfig.path].handle;
    }

    /**
    * Determines whether or not the current route should be reused
    * @param future The route the user is going to, as triggered by the router
    * @param curr The route the user is currently on
    * @returns boolean basically indicating true if the user intends to leave the current route
    */
    shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    console.log("deciding to reuse", "future", future.routeConfig, "current", curr.routeConfig, "return: ", future.routeConfig === curr.routeConfig);
    return future.routeConfig === curr.routeConfig;
    }

    /**
    * This nasty bugger finds out whether the objects are _traditionally_ equal to each other, like you might assume someone else would have put this function in vanilla JS already
    * One thing to note is that it uses coercive comparison (==) on properties which both objects have, not strict comparison (===)
    * Another important note is that the method only tells you if `compare` has all equal parameters to `base`, not the other way around
    * @param base The base object which you would like to compare another object to
    * @param compare The object to compare to base
    * @returns boolean indicating whether or not the objects have all the same properties and those properties are ==
    */
    private compareObjects(base: any, compare: any): boolean {

    // loop through all properties in base object
    for (let baseProperty in base) {

    // determine if comparrison object has that property, if not: return false
    if (compare.hasOwnProperty(baseProperty)) {
    switch(typeof base[baseProperty]) {
    // if one is object and other is not: return false
    // if they are both objects, recursively call this comparison function
    case 'object':
    if ( typeof compare[baseProperty] !== 'object' || !this.compareObjects(base[baseProperty], compare[baseProperty]) ) { return false; } break;
    // if one is function and other is not: return false
    // if both are functions, compare function.toString() results
    case 'function':
    if ( typeof compare[baseProperty] !== 'function' || base[baseProperty].toString() !== compare[baseProperty].toString() ) { return false; } break;
    // otherwise, see if they are equal using coercive comparison
    default:
    if ( base[baseProperty] != compare[baseProperty] ) { return false; }
    }
    } else {
    return false;
    }
    }

    // returns true only after false HAS NOT BEEN returned through all loops
    return true;
    }
    }

    行为

    此实现存储用户在路由器上访问的每条唯一路由仅一次。这将在用户在站点上的整个 session 期间继续添加到存储在内存中的组件。如果您想限制您存储的路线,可以在 shouldDetach 上进行。方法。它控制您保存哪些路线。

    示例

    假设您的用户从主页搜索某些内容,然后将他们导航到路径 search/:term , 可能看起来像 www.yourwebsite.com/search/thingsearchedfor .搜索页面包含一堆搜索结果。你想存储这条路线,以防他们想回来!现在他们点击搜索结果并导航到 view/:resultId ,您不想存储它,因为它们可能只会出现一次。有了上面的实现,我只需更改 shouldDetach方法!它可能如下所示:

    首先让我们创建一个我们想要存储的路径数组。

    private acceptedRoutes: string[] = ["search/:term"];

    现在,在 shouldDetach我们可以查看 route.routeConfig.path反对我们的阵列。

    shouldDetach(route: ActivatedRouteSnapshot): boolean {
    // check to see if the route's path is in our acceptedRoutes array
    if (this.acceptedRoutes.indexOf(route.routeConfig.path) > -1) {
    console.log("detaching", route);
    return true;
    } else {
    return false; // will be "view/:resultId" when user navigates to result
    }
    }

    因为 Angular 只会存储一个路由实例,所以这个存储是轻量级的,我们只会存储位于 search/:term 的组件。而不是所有其他人!

    附加链接

    尽管目前还没有太多文档,但这里有几个链接可以了解现有内容:

    Angular 文档: https://angular.io/docs/ts/latest/api/router/index/RouteReuseStrategy-class.html

    介绍文章: https://www.softwarearchitekt.at/post/2016/12/02/sticky-routes-in-angular-2-3-with-routereusestrategy.aspx

    nativescript-angular 的默认实现 路由重用策略 : https://github.com/NativeScript/nativescript-angular/blob/cb4fd3a/nativescript-angular/router/ns-route-reuse-strategy.ts

    关于javascript - 如何为 Angular 2 中的特定路由实现 RouteReuseStrategy shouldDetach,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41280471/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com