web-dev-qa-db-ja.com

Angular 2の特定のルートにRouteReuseStrategy shouldDetachを実装する方法

ルーティングを実装したAngular 2モジュールがあり、ナビゲーション時に状態を保存したいのですが。 1.検索式を使用してドキュメントを検索する2.結果の1つに移動する3. searchresultに戻る-サーバーと通信せずに

これは、RouteReuseStrategyを含めて可能です。問題は、ドキュメントを保存しないように実装するにはどうすればよいですか?

したがって、ルートパス「documents」の状態は保存され、ルートパス「documents /:id」の状態は保存されません。

78

受け入れられた答えに怖がらないでください。これは非常に簡単です。必要なものを簡単に答えます。私は少なくとも受け入れられた答えを読むことをお勧めします。それは非常に詳細に満ちているからです。

このソリューションは、受け入れられた答えのようなパラメータ比較を行いませんが、ルートのセットを保存するためにうまく機能します。

app.module.tsのインポート:

import { RouteReuseStrategy } from '@angular/router';
import { CustomReuseStrategy, Routing } from './shared/routing';

@NgModule({
//...
providers: [
    { provide: RouteReuseStrategy, useClass: CustomReuseStrategy },
  ]})

shared/routing.ts:

export class CustomReuseStrategy implements RouteReuseStrategy {
 routesToCache: string[] = ["dashboard"];
 storedRouteHandles = new Map<string, DetachedRouteHandle>();

 // Decides if the route should be stored
 shouldDetach(route: ActivatedRouteSnapshot): boolean {
    return this.routesToCache.indexOf(route.routeConfig.path) > -1;
 }

 //Store the information for the route we're destructing
 store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    this.storedRouteHandles.set(route.routeConfig.path, handle);
 }

//Return true if we have a stored route object for the next route
 shouldAttach(route: ActivatedRouteSnapshot): boolean {
    return this.storedRouteHandles.has(route.routeConfig.path);
 }

 //If we returned true in shouldAttach(), now return the actual route data for restoration
 retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
    return this.storedRouteHandles.get(route.routeConfig.path);
 }

 //Reuse the route if we're going to and from the same route
 shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    return future.routeConfig === curr.routeConfig;
 }
}
25
Chris Fremgen

遅延ロードされたモジュールでChris Fremgenの戦略を使用するには、CustomReuseStrategyクラスを次のように変更します。

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

export class CustomReuseStrategy implements RouteReuseStrategy {
  routesToCache: string[] = ["company"];
  storedRouteHandles = new Map<string, DetachedRouteHandle>();

  // Decides if the route should be stored
  shouldDetach(route: ActivatedRouteSnapshot): boolean {
     return this.routesToCache.indexOf(route.data["key"]) > -1;
  }

  //Store the information for the route we're destructing
  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
     this.storedRouteHandles.set(route.data["key"], handle);
  }

  //Return true if we have a stored route object for the next route
  shouldAttach(route: ActivatedRouteSnapshot): boolean {
     return this.storedRouteHandles.has(route.data["key"]);
  }

  //If we returned true in shouldAttach(), now return the actual route data for restoration
  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
     return this.storedRouteHandles.get(route.data["key"]);
  }

  //Reuse the route if we're going to and from the same route
  shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
     return future.routeConfig === curr.routeConfig;
  }
}

最後に、フィーチャモジュールのルーティングファイルで、キーを定義します。

{ path: '', component: CompanyComponent, children: [
    {path: '', component: CompanyListComponent, data: {key: "company"}},
    {path: ':companyID', component: CompanyDetailComponent},
]}

詳細 こちら

9
Uğur Dinç

受け入れられた回答(Corbfonによる)とChris Fremgenのより簡潔でわかりやすい説明に加えて、再利用戦略を使用する必要があるルートを処理するより柔軟な方法を追加したいと思います。

両方の回答は、キャッシュしたいルートを配列に保存し、現在のルートパスが配列内にあるかどうかを確認します。このチェックはshouldDetachメソッドで行われます。

ルートの名前を変更したい場合は、CustomReuseStrategyクラスのルート名も変更する必要があるため、このアプローチには柔軟性がありません。変更するのを忘れるか、チームの他の開発者がRouteReuseStrategyの存在すら知らずにルート名を変更することを決定するかもしれません。

キャッシュしたいルートを配列に保存する代わりに、RouterModuleオブジェクトを使用してdataに直接マークすることができます。この方法では、ルート名を変更しても、再利用戦略が適用されます。

{
  path: 'route-name-i-can-change',
  component: TestComponent,
  data: {
    reuseRoute: true
  }
}

そして、shouldDetachメソッドでそれを利用します。

shouldDetach(route: ActivatedRouteSnapshot): boolean {
  return route.data.reuseRoute === true;
}
5
Davor

次は仕事です!参照: https://www.cnblogs.com/lovesangel/p/7853364.html

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

export class CustomReuseStrategy implements RouteReuseStrategy {

    public static handlers: { [key: string]: DetachedRouteHandle } = {}

    private static waitDelete: string

    public static deleteRouteSnapshot(name: string): void {
        if (CustomReuseStrategy.handlers[name]) {
            delete CustomReuseStrategy.handlers[name];
        } else {
            CustomReuseStrategy.waitDelete = name;
        }
    }
   
    public shouldDetach(route: ActivatedRouteSnapshot): boolean {
        return true;
    }

   
    public store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
        if (CustomReuseStrategy.waitDelete && CustomReuseStrategy.waitDelete == this.getRouteUrl(route)) {
            // 如果待删除是当前路由则不存储快照
            CustomReuseStrategy.waitDelete = null
            return;
        }
        CustomReuseStrategy.handlers[this.getRouteUrl(route)] = handle
    }

    
    public shouldAttach(route: ActivatedRouteSnapshot): boolean {
        return !!CustomReuseStrategy.handlers[this.getRouteUrl(route)]
    }

    /** 从缓存中获取快照,若无则返回nul */
    public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
        if (!route.routeConfig) {
            return null
        }

        return CustomReuseStrategy.handlers[this.getRouteUrl(route)]
    }

   
    public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
        return future.routeConfig === curr.routeConfig &&
            JSON.stringify(future.params) === JSON.stringify(curr.params);
    }

    private getRouteUrl(route: ActivatedRouteSnapshot) {
        return route['_routerState'].url.replace(/\//g, '_')
    }
}
4
红兵伍

より有効で完全かつ再利用可能な別の実装。これは、遅延ロードされたモジュールを@UğurDinçとしてサポートし、@ Davorルートデータフラグを統合します。最良の改善は、ページの絶対パスに基づいた(ほぼ)一意の識別子の自動生成です。この方法では、すべてのページで自分で定義する必要はありません。

キャッシュするページにマークを付けますreuseRoute: true設定。 shouldDetachメソッドで使用されます。

{
  path: '',
  component: MyPageComponent,
  data: { reuseRoute: true },
}

これは、クエリパラメータを比較することなく、最も単純な戦略の実装です。

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

export class CustomReuseStrategy implements RouteReuseStrategy {

  storedHandles: { [key: string]: DetachedRouteHandle } = {};

  shouldDetach(route: ActivatedRouteSnapshot): boolean {
    return route.data.reuseRoute || false;
  }

  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    const id = this.createIdentifier(route);
    if (route.data.reuseRoute) {
      this.storedHandles[id] = handle;
    }
  }

  shouldAttach(route: ActivatedRouteSnapshot): boolean {
    const id = this.createIdentifier(route);
    const handle = this.storedHandles[id];
    const canAttach = !!route.routeConfig && !!handle;
    return canAttach;
  }

  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
    const id = this.createIdentifier(route);
    if (!route.routeConfig || !this.storedHandles[id]) return null;
    return this.storedHandles[id];
  }

  shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    return future.routeConfig === curr.routeConfig;
  }

  private createIdentifier(route: ActivatedRouteSnapshot) {
    // Build the complete path from the root to the input route
    const segments: UrlSegment[][] = route.pathFromRoot.map(r => r.url);
    const subpaths = ([] as UrlSegment[]).concat(...segments).map(segment => segment.path);
    // Result: ${route_depth}-${path}
    return segments.length + '-' + subpaths.join('/');
  }
}

これは、クエリパラメータも比較します。 compareObjectsは、@ Corbfonバージョンよりも少し改善されています。ベースオブジェクトと比較オブジェクトの両方のプロパティをループします。 lodash isEqualメソッドのような外部のより信頼性の高い実装を使用できることを忘れないでください。

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

interface RouteStorageObject {
  snapshot: ActivatedRouteSnapshot;
  handle: DetachedRouteHandle;
}

export class CustomReuseStrategy implements RouteReuseStrategy {

  storedRoutes: { [key: string]: RouteStorageObject } = {};

  shouldDetach(route: ActivatedRouteSnapshot): boolean {
    return route.data.reuseRoute || false;
  }

  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    const id = this.createIdentifier(route);
    if (route.data.reuseRoute && id.length > 0) {
      this.storedRoutes[id] = { handle, snapshot: route };
    }
  }

  shouldAttach(route: ActivatedRouteSnapshot): boolean {
    const id = this.createIdentifier(route);
    const storedObject = this.storedRoutes[id];
    const canAttach = !!route.routeConfig && !!storedObject;
    if (!canAttach) return false;

    const paramsMatch = this.compareObjects(route.params, storedObject.snapshot.params);
    const queryParamsMatch = this.compareObjects(route.queryParams, storedObject.snapshot.queryParams);

    console.log('deciding to attach...', route, 'does it match?');
    console.log('param comparison:', paramsMatch);
    console.log('query param comparison', queryParamsMatch);
    console.log(storedObject.snapshot, 'return: ', paramsMatch && queryParamsMatch);

    return paramsMatch && queryParamsMatch;
  }

  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
    const id = this.createIdentifier(route);
    if (!route.routeConfig || !this.storedRoutes[id]) return null;
    return this.storedRoutes[id].handle;
  }

  shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    return future.routeConfig === curr.routeConfig;
  }

  private createIdentifier(route: ActivatedRouteSnapshot) {
    // Build the complete path from the root to the input route
    const segments: UrlSegment[][] = route.pathFromRoot.map(r => r.url);
    const subpaths = ([] as UrlSegment[]).concat(...segments).map(segment => segment.path);
    // Result: ${route_depth}-${path}
    return segments.length + '-' + subpaths.join('/');
  }

  private compareObjects(base: any, compare: any): boolean {

    // loop through all properties
    for (const baseProperty in { ...base, ...compare }) {

      // 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:
            // tslint:disable-next-line triple-equals
            if (base[baseProperty] != compare[baseProperty]) {
              return false;
            }
        }
      } else {
        return false;
      }
    }

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

一意のキーを生成する最良の方法があれば、私の答えをコメントして、コードを更新します。

ソリューションを共有してくれたすべての人に感謝します。

1
McGiogen