webentwicklung-frage-antwort-db.com.de

So implementieren Sie RouteReuseStrategy sollte für bestimmte Routen in Angular 2

Ich habe ein Angular 2 Modul, in dem ich Routing implementiert habe und möchte, dass die Status beim Navigieren gespeichert werden. Der Benutzer sollte in der Lage sein: 1. Dokumente mit einer Suchformel zu suchen 2. zu einer zu navigieren der Ergebnisse 3. Zum Suchergebnis zurück navigieren - ohne mit dem Server zu kommunizieren

Dies ist einschließlich RouteReuseStrategy möglich. Die Frage ist: Wie implementiere ich, dass das Dokument nicht gespeichert werden soll?

Der Status des Routenpfads "Dokumente" sollte also gespeichert werden, und der Status des Routenpfads "Dokumente /: ID" sollte NICHT gespeichert werden.

83

Hey Anders, tolle Frage!

Ich habe fast den gleichen Anwendungsfall wie Sie und wollte das Gleiche tun! Benutzersuche> Ergebnisse abrufen> Benutzer navigiert zum Ergebnis> Benutzer navigiert zurück> [~ # ~] boom [~ # ~] blitzschnelle Rückkehr zu den Ergebnissen, aber Sie möchten nicht das spezifische Ergebnis speichern, zu dem der Benutzer navigiert hat.

tl; dr

Sie benötigen eine Klasse, die RouteReuseStrategy implementiert und Ihre Strategie in ngModule bereitstellt. Wenn Sie die Route beim Speichern ändern möchten, ändern Sie die Funktion shouldDetach. Wenn es true zurückgibt, speichert Angular) die Route. Wenn Sie ändern möchten, wenn die Route angehängt ist, ändern Sie die Funktion shouldAttach. Wenn shouldAttach gibt true zurück, Angular verwendet die gespeicherte Route anstelle der angeforderten Route. Hier ist ein Plunker , mit dem Sie herumspielen können.

Über RouteReuseStrategy

Wenn Sie diese Frage gestellt haben, verstehen Sie bereits, dass RouteReuseStrategy es Ihnen ermöglicht, Angular nichtzu zerstören, sondern es tatsächlich für eine erneute Speicherung zu speichern -Rendering zu einem späteren Zeitpunkt. Das ist cool, weil es ermöglicht:

  • VerringerteServer-Aufrufe
  • ErhöhteGeschwindigkeit
  • [~ # ~] und [~ # ~]Die Komponente wird standardmäßig in dem Zustand wiedergegeben, in dem sie belassen wurde

Letzteres ist wichtig, wenn Sie beispielsweise eine Seite vorübergehend verlassen möchten, obwohl der Benutzer eine lot Zeichenfolge eingegeben hat. Unternehmensanwendungen werden diese Funktion wegen der übermäßigenAnzahl von Formularen lieben!

Das habe ich mir ausgedacht, um das Problem zu lösen. Wie Sie sagten, müssen Sie das RouteReuseStrategy verwenden, das von @ angular/router in den Versionen 3.4.1 und höher angeboten wird.

[~ # ~] todo [~ # ~]

First Stellen Sie sicher, dass Ihr Projekt über @ angle/router Version 3.4.1 oder höher verfügt.

Next , erstelle eine Datei, die deine Klasse beherbergt, die RouteReuseStrategy implementiert. Ich rief meinen reuse-strategy.ts an und legte ihn zur sicheren Aufbewahrung in den Ordner /app. Vorerst sollte diese Klasse so aussehen:

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

export class CustomReuseStrategy implements RouteReuseStrategy {
}

(Mach dir keine Sorgen um deine TypeScript-Fehler, wir werden gleich alles lösen.)

Beenden Sie die Grundlagenarbeit , indem Sie die Klasse für Ihren app.module angeben. Beachten Sie, dass Sie CustomReuseStrategy noch nicht geschrieben haben, aber trotzdem import von reuse-strategy.ts ausführen sollten. Auch import { RouteReuseStrategy } from '@angular/router';

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

Das letzte Stück schreibt die Klasse, die steuert, ob Routen getrennt, gespeichert, abgerufen und wieder angehängt werden. Bevor wir zum alten copy/pastekommen, werde ich hier eine kurze Erklärung der Mechanik geben, so wie ich sie verstehe. Verweisen Sie auf den folgenden Code für die Methoden, die ich beschreibe, und natürlich gibt es eine Menge Dokumentation im Code .

  1. Beim Navigieren wird shouldReuseRoute ausgelöst. Dieser ist ein bisschen seltsam für mich, aber wenn er true zurückgibt, wird die Route, auf der Sie sich gerade befinden, tatsächlich wiederverwendet und keine der anderen Methoden wird ausgelöst. Ich gebe nur false zurück, wenn der Benutzer wegnavigiert.
  2. Wenn shouldReuseRoutefalse zurückgibt, wird shouldDetach ausgelöst. shouldDetach legt fest, ob Sie die Route speichern möchten oder nicht, und gibt ein boolean zurück, das dies anzeigt. Hier sollten Sie entscheiden, Pfade zu speichern/nicht zu speichern . Dies würde ich tun, indem Sie ein Array von Pfaden überprüfen, die Sie möchtengespeichert haben gegen route.routeConfig.path und false zurück, wenn das path nicht im Array vorhanden ist.
  3. Wenn shouldDetachtrue zurückgibt, wird store ausgelöst. In diesem Fall können Sie alle gewünschten Informationen zur Route speichern. Was auch immer Sie tun, Sie müssen das DetachedRouteHandle speichern, da dies das ist, was Angular verwendet, um Ihre gespeicherte Komponente später zu identifizieren. Unten speichere ich sowohl das DetachedRouteHandle und das ActivatedRouteSnapshot in eine Variable lokal für meine Klasse.

Wir haben also die Logik für die Speicherung gesehen, aber wie sieht es mit der Navigation von zueiner Komponente aus? Wie entscheidet Angular Ihre Navigation abzufangen und die gespeicherte an ihre Stelle zu setzen?

  1. Nachdem shouldReuseRoutefalse zurückgegeben hat, wird shouldAttach ausgeführt. Dies ist Ihre Chance, herauszufinden, ob Sie die Komponente neu generieren oder im Speicher verwenden möchten. Wenn Sie eine gespeicherte Komponente wiederverwenden möchten, geben Sie true zurück und Sie sind auf dem besten Weg!
  2. Nun fragt Sie Angular, "welche Komponente sollen wir verwenden?", Was Sie angeben, indem Sie DetachedRouteHandle dieser Komponente von retrieve zurückgeben.

Das ist so ziemlich die ganze Logik, die Sie brauchen! Im Code für reuse-strategy.ts unten habe ich Ihnen auch eine nützliche Funktion hinterlassen, die zwei Objekte vergleicht. Ich verwende es, um den route.params und den route.queryParams der zukünftigen Route mit dem gespeicherten zu vergleichen. Wenn alle übereinstimmen, möchte ich die gespeicherte Komponente verwenden, anstatt eine neue zu generieren. Aber wie Sie es tun, ist bis zu Ihnen!

reuse-strategy.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;
    }
}

Verhalten

Diese Implementierung speichert jede eindeutige Route, die der Benutzer auf dem Router besucht, genau einmal. Dies wird weiterhin zu den Komponenten hinzugefügt, die während der Benutzersitzung auf der Site im Speicher gespeichert sind. Wenn Sie die von Ihnen gespeicherten Routen einschränken möchten, verwenden Sie die Methode shouldDetach. Es steuert, welche Routen Sie speichern.

Beispiel

Angenommen, Ihr Benutzer sucht auf der Startseite nach etwas, das ihn zum Pfad search/:term navigiert, der möglicherweise wie www.yourwebsite.com/search/thingsearchedfor aussieht. Die Suchseite enthält eine Reihe von Suchergebnissen. Sie möchten diese Route speichern, falls sie darauf zurückkommen möchten! Jetzt klicken sie auf ein Suchergebnis und navigieren zu view/:resultId, den Sie nichtspeichern möchten, da sie wahrscheinlich nur einmal dort sein werden. Mit der obigen Implementierung würde ich einfach die shouldDetach -Methode ändern! So könnte es aussehen:

Zuerst machen wir ausein Array von Pfaden, die wir speichern wollen.

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

jetzt können wir in shouldDetach den route.routeConfig.path mit unserem Array vergleichen.

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
    }
}

Da Angular nur eine Instanzeiner Route speichert, ist dieser Speicher kompakt und wir speichern nur die Komponente, die sich unter search/:term und befindet nicht alle anderen!

Additional Links

Obwohl es noch nicht viel Dokumentation gibt, gibt es hier ein paar Links zu dem, was existiert:

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

Intro-Artikel: https://www.softwarearchitekt.at/post/2016/12/02/sticky-routes-in-angular-2-3-with-routereusestrategy.aspx

160
Corbfon

Lassen Sie sich von der akzeptierten Antwort nicht einschüchtern, das ist ziemlich einfach. Hier ist eine schnelle Antwort, was Sie brauchen. Ich würde zumindest empfehlen, die akzeptierte Antwort zu lesen, da sie sehr detailliert ist.

Diese Lösung führt keinen Parametervergleich wie die akzeptierte Antwort durch, funktioniert jedoch zum Speichern einer Reihe von Routen.

app.module.ts importiert:

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;
 }
}
30
Chris Fremgen

Ändern Sie die CustomReuseStrategy-Klasse folgendermaßen, um die Strategie von Chris Fremgen mit langsam geladenen Modulen zu verwenden:

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;
  }
}

definieren Sie schließlich in den Routing-Dateien Ihrer Feature-Module Ihre Schlüssel:

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

Weitere Infos hier .

10
Uğur Dinç

Zusätzlich zu der akzeptierten Antwort (von Corbfon) und der kürzeren und einfacheren Erklärung von Chris Fremgen möchte ich eine flexiblere Methode für den Umgang mit Routen hinzufügen, die die Wiederverwendungsstrategie verwenden sollten.

In beiden Antworten werden die Routen, die zwischengespeichert werden sollen, in einem Array gespeichert. Anschließend wird überprüft, ob der aktuelle Routenpfad im Array enthalten ist oder nicht. Diese Prüfung erfolgt in der shouldDetach Methode.

Ich finde diesen Ansatz unflexibel, denn wenn wir den Namen der Route ändern möchten, müssen wir uns daran erinnern, auch den Routennamen in unserer Klasse CustomReuseStrategy zu ändern. Möglicherweise vergessen wir die Änderung, oder ein anderer Entwickler in unserem Team entscheidet sich für eine Änderung des Routennamens, ohne überhaupt über die Existenz von RouteReuseStrategy Bescheid zu wissen.

Anstatt die Routen, die wir zwischenspeichern möchten, in einem Array zu speichern, können wir sie mit dem Objekt RouterModule direkt in data markieren. Auf diese Weise würde die Wiederverwendungsstrategie auch dann angewendet, wenn wir den Routennamen ändern.

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

Und dann machen wir in der shouldDetach -Methode davon Gebrauch.

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

folgendes ist Arbeit! Referenz: 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
红兵伍

Eine weitere Implementierung ist gültiger, vollständiger und wiederverwendbarer. Dieser unterstützt faul geladene Module wie @ Uğur Dinç und integriert das @Davor Routendaten-Flag. Die beste Verbesserung ist die automatische Generierung eines (fast) eindeutigen Bezeichners basierend auf dem absoluten Seitenpfad. Auf diese Weise müssen Sie es nicht auf jeder Seite selbst definieren.

Markieren Sie jede Seite, die Sie zwischenspeichern möchten. Einstellung reuseRoute: true. Es wird in der shouldDetach Methode verwendet.

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

Dies ist die einfachste Strategieimplementierung, ohne Abfrageparameter zu vergleichen.

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('/');
  }
}

Dieser vergleicht auch die Abfrageparameter. compareObjects hat eine kleine Verbesserung gegenüber der @ Corbfon-Version: Durchlaufen Sie die Eigenschaften von Basis- und Vergleichsobjekten. Denken Sie daran, dass Sie eine externe und zuverlässigere Implementierung wie die lodash isEqual -Methode verwenden können.

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;
  }
}

Wenn Sie die beste Möglichkeit haben, eindeutige Schlüssel zu generieren, kommentieren Sie meine Antwort, ich werde den Code aktualisieren.

Vielen Dank an alle, die ihre Lösung geteilt haben.

1
McGiogen