Come implementare RouteReuseStrategy shouldDetach per rotte specifiche in Angular 2

Ho un modulo Angular 2 in cui ho implementato il routing e vorrei che gli stati memorizzati durante la navigazione. L’utente dovrebbe essere in grado di: 1. cercare i documenti utilizzando un modulo di ricerca 2. passare a uno dei risultati 3. tornare a cercare risultati – senza comunicare con il server

Questo è ansible, incluso RouteReuseStrategy. La domanda è: come posso implementare che il documento non debba essere archiviato?

Quindi lo stato del percorso “documenti” deve essere memorizzato e lo stato del percorso “documenti /: id” non deve essere memorizzato?

Hey Anders, bella domanda!

Ho quasi lo stesso caso d’uso come te e volevo fare la stessa cosa! Ricerca utente> ottieni risultati> L’utente naviga verso il risultato> L’utente torna indietro> BOOM sfolgorante restituisce rapidamente i risultati , ma non si desidera memorizzare il risultato specifico a cui l’utente ha navigato.

tl; dr

È necessario disporre di una class che implementa RouteReuseStrategy e fornire la strategia in ngModule . Se si desidera modificare quando la rotta è memorizzata, modificare la funzione shouldDetach . Quando restituisce true , Angular memorizza la rotta. Se si desidera modificare quando il percorso è collegato, modificare la funzione shouldAttach . Quando shouldAttach restituire true, Angular utilizzerà la rotta memorizzata al posto della rotta richiesta. Ecco un Plunker con cui giocare.

Informazioni su RouteReuseStrategy

Avendo fatto questa domanda, capisci già che RouteReuseStrategy ti permette di dire ad Angular2 di non distruggere un componente, ma di salvarlo per renderlo in un secondo momento. È bello perché consente:

  • Diminuzioni delle chiamate al server
  • Aumento della velocità
  • E il componente viene visualizzato, per impostazione predefinita, nello stesso stato in cui era rimasto

Quest’ultimo è importante se si desidera, ad esempio, lasciare temporaneamente una pagina anche se l’utente ha inserito molto testo in esso. Le applicazioni aziendali adoreranno questa funzionalità a causa dell’eccessiva quantità di moduli!

Questo è quello che mi è venuto in mente per risolvere il problema. Come hai detto, devi utilizzare RouteReuseStrategy offerto da @ RouteReuseStrategy / router nelle versioni 3.4.1 e successive.

FARE

Prima assicurati che il tuo progetto abbia @ angular / router versione 3.4.1 o successiva.

Quindi , crea un file che ospiterà la tua class che implementa RouteReuseStrategy . Ho chiamato il mio reuse-strategy.ts e l’ho messo nella cartella /app per sicurezza. Per ora, questa class dovrebbe essere simile a:

 import { RouteReuseStrategy } from '@angular/router'; export class CustomReuseStrategy implements RouteReuseStrategy { } 

(non preoccuparti degli errori TypeScript, stiamo per risolvere tutto)

Completa le basi fornendo la class al tuo app.module . Nota che non hai ancora scritto CustomReuseStrategy , ma dovresti procedere e import da reuse-strategy.ts lo stesso. import { RouteReuseStrategy } from '@angular/router'; anche import { RouteReuseStrategy } from '@angular/router';

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

L’ultimo pezzo sta scrivendo la class che controllerà se le rotte vengono staccate, memorizzate, recuperate e ricollegate. Prima di arrivare alla vecchia copia / incolla , farò una breve spiegazione della meccanica qui, poiché li capisco. Fai riferimento al codice qui sotto per i metodi che sto descrivendo e, naturalmente, c’è abbondanza di documentazione nel codice .

  1. Quando shouldReuseRoute , shouldReuseRoute . Questo è un po ‘strano per me, ma se restituisce true , in realtà riutilizza la rotta in cui ti trovi e nessuno degli altri metodi viene triggersto. Ho appena restituito false se l’utente sta navigando.
  2. Se shouldReuseRoute restituisce false , shouldDetach . shouldDetach determina se si desidera o meno memorizzare la rotta e restituisce un valore boolean indica tanto. È qui che dovresti decidere di memorizzare / non memorizzare i percorsi , cosa che farei controllando un array di percorsi che vuoi archiviato su route.routeConfig.path e restituendo false se il path non esiste nell’array.
  3. Se shouldDetach restituisce true , viene shouldDetach store , che rappresenta un’opportunità per archiviare le informazioni che desideri sul percorso. Qualunque cosa tu faccia, dovrai salvare DetachedRouteHandle perché questo è ciò che Angular usa per identificare in seguito il tuo componente memorizzato. Sotto, memorizzo sia DetachedRouteHandle che ActivatedRouteSnapshot in una variabile locale alla mia class.

Quindi, abbiamo visto la logica per l’archiviazione, ma per quanto riguarda la navigazione verso un componente? In che modo Angular decide di intercettare la tua navigazione e inserire quella memorizzata al suo posto?

  1. Di nuovo, dopo che shouldReuseRoute ha restituito false , shouldAttach viene eseguito, che è la possibilità di capire se si desidera rigenerare o utilizzare il componente in memoria. Se vuoi riutilizzare un componente memorizzato, restituisci true e sei sulla buona strada!
  2. Ora Angular ti chiederà “quale componente vuoi che usiamo?”, Che indicherai restituendo DetachedRouteHandle del componente da retrieve .

È praticamente tutta la logica di cui hai bisogno! Nel codice per reuse-strategy.ts , di seguito, ho anche lasciato una funzione elegante che confronterà due oggetti. Lo uso per confrontare il percorso route.params e route.queryParams con quelli memorizzati. Se tutti corrispondono, voglio utilizzare il componente memorizzato invece di generarne uno nuovo. Ma come lo fai dipende da te!

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

Comportamento

Questa implementazione memorizza ogni percorso univoco che l’utente visita sul router esattamente una volta. Questo continuerà ad aggiungere ai componenti archiviati in memoria durante la sessione dell’utente sul sito. Se desideri limitare i percorsi che shouldDetach , il luogo per farlo è il metodo shouldDetach . Controlla quali rotte si salva.

Esempio

Supponi che il tuo utente cerchi qualcosa dalla home page, che li indirizza alla search/:term percorso search/:term , che potrebbe apparire come www.yourwebsite.com/search/thingsearchedfor . La pagina di ricerca contiene una serie di risultati di ricerca. Ti piacerebbe memorizzare questo percorso, nel caso in cui vogliano tornare ad esso! Ora fanno clic su un risultato di ricerca e vengono view/:resultId per view/:resultId , che non si desidera archiviare, visto che probabilmente saranno lì solo una volta. Con l’implementazione di cui sopra, vorrei semplicemente cambiare il metodo shouldDetach ! Ecco come potrebbe essere:

Innanzitutto facciamo una serie di percorsi che vogliamo archiviare.

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

ora, in shouldDetach possiamo controllare il route.routeConfig.path contro il nostro array.

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

Poiché Angular memorizzerà solo un’istanza di un percorso, questo spazio di archiviazione sarà leggero e verrà memorizzato solo il componente situato in search/:term e non tutti gli altri!

Link aggiuntivi

Sebbene non ci sia ancora molta documentazione, ecco un paio di collegamenti a ciò che esiste:

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

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

Non essere intimidito dalla risposta accettata, questo è abbastanza semplice. Ecco una rapida risposta a ciò di cui hai bisogno. Raccomanderei almeno di leggere la risposta accettata, poiché è ricca di dettagli.

Questa soluzione non esegue alcun confronto di parametri come la risposta accettata, ma funzionerà correttamente per la memorizzazione di una serie di percorsi.

Importazioni di app.module.ts:

 import { RouteReuseStrategy } from '@angular/router'; import { CustomReuseStrategy, Routing } from './shared/routing'; @NgModule({ //... providers: [ { provide: RouteReuseStrategy, useClass: CustomReuseStrategy }, ]}) 

condiviso / routing.ts:

 export class CustomReuseStrategy implements RouteReuseStrategy { routesToCache: string[] = ["dashboard"]; storedRouteHandles = new Map(); // 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; } } 

Per utilizzare la strategia di Chris Fremgen con moduli caricati lentamente, modificare la class CustomReuseStrategy nel modo seguente:

 import {ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy} from '@angular/router'; export class CustomReuseStrategy implements RouteReuseStrategy { routesToCache: string[] = ["company"]; storedRouteHandles = new Map(); // 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; } } 

infine, nei file di routing dei moduli funzione, definisci le tue chiavi:

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

Maggiori informazioni qui .

il seguente è lavoro! riferimento: 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, '_') } }