rutas redireccionar hijas javascript angular typescript angular-ui-router

javascript - redireccionar - Cómo implementar RouteReuseStrategy shouldDetach para rutas específicas en Angular 2



rutas angular 6 (5)

Tengo un módulo Angular 2 en el que he implementado el enrutamiento y me gustaría que los estados se almacenen al navegar. El usuario debe ser capaz de: 1. buscar documentos utilizando una fórmula de búsqueda 2. navegar a uno de los resultados 3. navegar de nuevo al resultado de búsqueda, sin comunicarse con el servidor

Esto es posible incluyendo RouteReuseStrategy. La pregunta es: ¿Cómo implemento que el documento no debe almacenarse?

Entonces, ¿el estado de los "documentos" de la ruta de ruta debe almacenarse y el estado de los "documentos /: id" de la ruta de ruta NO debe almacenarse?


Además de la respuesta aceptada (por Corbfon) y la explicación más corta y directa de Chris Fremgen, quiero agregar una forma más flexible de manejar rutas que deberían usar la estrategia de reutilización.

Ambas respuestas almacenan las rutas que queremos almacenar en caché en una matriz y luego verifican si la ruta de ruta actual está en la matriz o no. Esta comprobación se realiza en el método shouldDetach .

Este enfoque me parece inflexible porque si queremos cambiar el nombre de la ruta, deberíamos recordar también cambiar el nombre de la ruta en nuestra clase CustomReuseStrategy . Podemos olvidar cambiarlo o algún otro desarrollador de nuestro equipo puede decidir cambiar el nombre de la ruta sin saber siquiera la existencia de RouteReuseStrategy .

En lugar de almacenar las rutas que queremos almacenar en caché en una matriz, podemos marcarlas directamente en RouterModule usando data objeto de data . De esta manera, incluso si cambiamos el nombre de la ruta, la estrategia de reutilización aún se aplicaría.

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

Y luego, en el método shouldDetach , hacemos un uso de eso.

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


Hola Anders, ¡gran pregunta!

Tengo casi el mismo caso de uso que tú, ¡y quería hacer lo mismo! Búsqueda de usuario> obtener resultados> El usuario navega hacia el resultado> El usuario navega hacia atrás> BOOM vuelve rápidamente a los resultados , pero no desea almacenar el resultado específico al que navegó el usuario.

tl; dr

RouteReuseStrategy tener una clase que implemente RouteReuseStrategy y proporcione su estrategia en el ngModule . Si desea modificar cuando se almacena la ruta, modifique la función shouldDetach . Cuando vuelve true , Angular almacena la ruta. Si desea modificar cuando se adjunta la ruta, modifique la función shouldAttach . Cuando shouldAttach devuelve verdadero, Angular utilizará la ruta almacenada en lugar de la ruta solicitada. Aquí hay un Plunker para que juegues.

Sobre RouteReuseStrategy

Al haber hecho esta pregunta, ya comprende que RouteReuseStrategy le permite decirle a Angular que no destruya un componente, sino que lo guarde para volver a renderizarlo en una fecha posterior. Eso es genial porque permite:

  • Disminución de llamadas al servidor
  • Mayor velocidad
  • Y el componente representa, por defecto, en el mismo estado en que se dejó

Esa última es importante si desea, por ejemplo, abandonar una página temporalmente aunque el usuario haya ingresado una gran cantidad de texto. ¡Las aplicaciones empresariales adorarán esta característica debido a la cantidad excesiva de formularios!

Esto es lo que se me ocurrió para resolver el problema. Como dijiste, debes hacer uso de la RouteReuseStrategy ofrecida por @ angular / router en las versiones 3.4.1 y superiores.

QUE HACER

Primero asegúrese de que su proyecto tenga @ angular / router versión 3.4.1 o superior.

A continuación , cree un archivo que albergará a su clase que implementa RouteReuseStrategy . Llamé a mine reuse-strategy.ts y lo coloqué en la carpeta /app para su custodia. Por ahora, esta clase debería verse así:

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

(no se preocupe por sus errores de TypeScript, estamos a punto de resolverlo todo)

Termine las bases proporcionando la clase a su app.module . Tenga en cuenta que aún no ha escrito CustomReuseStrategy , pero debe seguir adelante e import de reuse-strategy.ts todos CustomReuseStrategy . También import { RouteReuseStrategy } from ''@angular/router'';

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

La pieza final es escribir la clase que controlará si las rutas se desconectan, almacenan, recuperan y vuelven a conectar. Antes de llegar al viejo copiar / pegar , haré una breve explicación de la mecánica aquí, tal como los entiendo. Consulte el código a continuación para conocer los métodos que estoy describiendo y, por supuesto, hay mucha documentación en el código .

  1. Cuando navega, shouldReuseRoute incendios. Este es un poco extraño para mí, pero si vuelve true , entonces reutiliza la ruta en la que estás actualmente y ninguno de los otros métodos se dispara. Solo devuelvo falso si el usuario se está alejando.
  2. Si shouldReuseRoute devuelve false , shouldDetach dispara. shouldDetach determina si desea o no almacenar la ruta y devuelve un valor boolean indica tanto. Aquí es donde debe decidir almacenar / no almacenar rutas , lo que haría al verificar una matriz de rutas que desea almacenar contra route.routeConfig.path y devolver false si la path no existe en la matriz.
  3. Si shouldDetach devuelve true , se dispara store , que es una oportunidad para que almacene cualquier información que desee sobre la ruta. Hagas lo que hagas, deberás almacenar DetachedRouteHandle porque eso es lo que Angular usa para identificar tu componente almacenado más adelante. A continuación, almaceno el DetachedRouteHandle y el ActivatedRouteSnapshot en una variable local de mi clase.

Entonces, hemos visto la lógica para el almacenamiento, pero ¿qué pasa con la navegación a un componente? ¿Cómo decide Angular interceptar su navegación y colocar la almacenada en su lugar?

  1. Nuevamente, después de que shouldReuseRoute ha devuelto false , shouldAttach ejecuta, que es su oportunidad de averiguar si desea regenerar o usar el componente en la memoria. Si desea reutilizar un componente almacenado, devuelva true y ¡ya está en camino!
  2. Ahora Angular le preguntará "¿qué componente desea que usemos?", Que indicará al devolver DetachedRouteHandle de ese componente de la retrieve .

¡Esa es casi toda la lógica que necesitas! En el código para reuse-strategy.ts , a continuación, también te dejo una ingeniosa función que comparará dos objetos. Lo uso para comparar la ruta futura para route.params y route.queryParams con la almacenada. Si todos coinciden, quiero usar el componente almacenado en lugar de generar uno nuevo. ¡Pero cómo lo haces depende de ti!

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

Comportamiento

Esta implementación almacena cada ruta única que el usuario visita en el enrutador exactamente una vez. Esto continuará agregando a los componentes almacenados en la memoria durante la sesión del usuario en el sitio. Si desea limitar las rutas que almacena, el lugar para hacerlo es el método shouldDetach . Controla qué rutas guarda.

Ejemplo

Supongamos que su usuario busca algo desde la página de inicio, que lo dirige a la ruta de search/:term , que puede aparecer como www.yourwebsite.com/search/thingsearchedfor . La página de búsqueda contiene un montón de resultados de búsqueda. ¡Te gustaría almacenar esta ruta, en caso de que quieran volver a ella! Ahora hacen clic en un resultado de búsqueda y navegan para view/:resultId , que no desea almacenar, ya que probablemente solo estarán allí una vez. ¡Con la implementación anterior en su lugar, simplemente cambiaría el método shouldDetach ! Así es como podría verse:

Primero , hagamos una serie de caminos que queremos almacenar.

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

ahora, en shouldDetach podemos verificar el route.routeConfig.path contra nuestra matriz.

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

Debido a que Angular solo almacenará una instancia de una ruta, este almacenamiento será liviano y solo almacenaremos el componente ubicado en search/:term y no todos los demás.

Enlaces Adicionales

Aunque todavía no hay mucha documentación, aquí hay un par de enlaces a lo que existe:

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

Artículo de introducción: https://www.softwarearchitekt.at/post/2016/12/02/sticky-routes-in-angular-2-3-with-routereusestrategy.aspx


No se deje intimidar por la respuesta aceptada, esto es bastante sencillo. Aquí hay una respuesta rápida a lo que necesita. Recomendaría al menos leer la respuesta aceptada, ya que está llena de grandes detalles.

Esta solución no hace ninguna comparación de parámetros como la respuesta aceptada, pero funcionará bien para almacenar un conjunto de rutas.

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


Para usar la estrategia de Chris Fremgen con módulos con carga lenta, modifique la clase CustomReuseStrategy a lo siguiente:

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

finalmente, en los archivos de enrutamiento de sus módulos de funciones, defina sus claves:

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

Más información here .


lo siguiente es trabajo! referencia: 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, ''_'') } }