안녕하세요 Anders, 좋은 질문입니다!
나는 당신과 거의 같은 사용 사례를 가지고 있으며 같은 일을하고 싶었습니다! 사용자 검색> 결과 가져 오기> 사용자가 결과로 이동> 사용자가 뒤로 이동> BOOM 결과로 빠르게 돌아가 지만 사용자가 탐색 한 특정 결과를 저장하고 싶지 않습니다.
tl; dr
RouteReuseStrategy
.NET Framework에서 전략 을 구현 하고 제공 하는 클래스가 있어야 합니다 ngModule
. 경로가 저장 될 때 수정하려면 shouldDetach
기능을 수정하십시오 . 반환 true
되면 Angular는 경로를 저장합니다. 루트가 부착 된 시점을 수정하려면 shouldAttach
기능을 수정하십시오 . shouldAttach
true를 반환 하면 Angular는 요청 된 경로 대신 저장된 경로를 사용합니다. 여기 에 플레이 할 수 있는 Plunker 가 있습니다.
RouteReuseStrategy 정보
이 질문을 통해 RouteReuseStrategy를 사용하면 Angular 가 구성 요소를 파괴 하지 않고 실제로 나중에 다시 렌더링하기 위해 저장 하도록 지시 할 수 있음을 이미 이해했습니다 . 다음을 허용하기 때문에 멋집니다.
- 서버 호출 감소
- 증가 된 속도
- 그리고 구성 요소는 기본적으로 남은 상태로 렌더링됩니다.
마지막 페이지는 사용자가 페이지에 많은 텍스트를 입력 했음에도 불구하고 일시적으로 페이지를 떠나려는 경우에 중요 합니다. 과도한 양의 양식 때문에 엔터프라이즈 응용 프로그램은이 기능을 좋아할 것입니다 !
이것이 제가 문제를 해결하기 위해 생각 해낸 것입니다. 말했듯이 RouteReuseStrategy
버전 3.4.1 이상에서 @ angular / router가 제공하는을 사용해야합니다 .
할 것
먼저 프로젝트에 @ angular / router 버전 3.4.1 이상이 있는지 확인하십시오.
다음으로 , 구현하는 클래스를 저장할 파일을 만듭니다 RouteReuseStrategy
. 나는 내 전화를 걸어 보관을 위해 폴더에 reuse-strategy.ts
넣었습니다 /app
. 현재이 클래스는 다음과 같아야합니다.
import { RouteReuseStrategy } from '@angular/router';
export class CustomReuseStrategy implements RouteReuseStrategy {
}
(TypeScript 오류에 대해 걱정하지 마십시오. 모든 것을 해결하려고합니다)
에 수업을 제공 하여 기초 작업을 마칩니다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
하고,이 경우는 false를 반환 path
배열에 존재하지 않습니다.
- 경우
shouldDetach
반환 true
, store
당신은 당신이 경로에 대해 원하는 정보를 모두 저장할 수있는 기회 인, 발생합니다. 무엇을 하든지간에 DetachedRouteHandle
Angular가 나중에 저장된 구성 요소를 식별하는 데 사용 하는 것이기 때문에 를 저장해야합니다 . 다음, 나는 모두 저장 DetachedRouteHandle
하고 ActivatedRouteSnapshot
내 클래스에 변수 지역에 있습니다.
그래서 우리는 저장 로직을 보았지만 컴포넌트로 이동 하는 것은 어떨까요? Angular는 내비게이션을 가로 채서 저장된 내비게이션을 제자리에두기로 결정합니까?
- 다시, 후
shouldReuseRoute
돌아왔다 false
, shouldAttach
당신은 재생 또는 메모리에있는 구성 요소를 사용할지 여부를 알아낼 수있는 기회 인 실행합니다. 저장된 구성 요소를 재사용 true
하려면 돌아 가면됩니다 .
- 해당 구성 요소의 반환에 의해 표시되는, "당신은 우리가 사용하려는 않는 구성 요소?"를 묻습니다 이제 각도
DetachedRouteHandle
에서 retrieve
.
그것은 당신이 필요로하는 거의 모든 논리입니다! 아래의에 대한 코드에서 reuse-strategy.ts
두 개체를 비교할 수있는 멋진 함수도 남겼습니다. 나는 미래 경로의 비교에 사용 route.params
하고 route.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;
}
}
행동
이 구현은 사용자가 라우터에서 정확히 한 번 방문하는 모든 고유 경로를 저장합니다. 이것은 사이트의 사용자 세션 동안 메모리에 저장된 구성 요소에 계속 추가됩니다. 저장하는 경로를 제한하려면 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 - 각도의 기본 구현 RouteReuseStrategy : https://github.com/NativeScript/nativescript-angular/blob/cb4fd3a/nativescript-angular/router/ns-route-reuse-strategy.ts