アンダースさん、すばらしい質問です。
私はあなたとほとんど同じユースケースを持っており、同じことをしたかったのです!ユーザー検索>結果を取得>ユーザーが結果に移動>ユーザーが戻って移動> BOOM 高速で結果にすばやく戻るにが、ユーザーが移動した特定の結果を保存したくない場合。
tl; dr
でRouteReuseStrategy
戦略を実装および提供するクラスが必要ngModule
です。ルートが保存されたときに変更する場合は、shouldDetach
関数を変更します。true
Angular がを返すと、ルートが保存されます。ルートがアタッチされているときに変更する場合は、shouldAttach
機能を変更します。場合はshouldAttach
trueを返す、角度は、要求されたルートの場所に保存されたルートを使用します。ここであなたが遊んでいるためのプランカーがあります。
RouteReuseStrategyについて
この質問をしたことで、RouteReuseStrategyを使用すると、Angularにコンポーネントを破棄せずに、後で再レンダリングするために保存するように指示できることをすでに理解しています。それができるのでそれはクールです:
- サーバー呼び出しの減少
- 増加スピード
- ANDコンポーネントは、デフォルトで、それが残されたのと同じ状態でレンダリングします
最後の1つは、たとえば、ユーザーが大量のテキストを入力したにもかかわらず、一時的にページを離れたい場合に重要です。フォームの数が多すぎるため、エンタープライズアプリケーションはこの機能を気に入っています。
これは私が問題を解決するために思いついたものです。あなたが言ったように、あなたはを利用する必要がありますRouteReuseStrategy
はバージョン3.4.1以降で@ angular / routerによって提供されたupます。
TODO
最初、プロジェクトに@ 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
ます。順調です。
- これで、Angularから「どのコンポーネントを使用しますか?」と尋ねられます。これは、そのコンポーネントの
DetachedRouteHandle
fromを返すことで示しますretrieve
。
必要なロジックはこれだけです。reuse-strategy.ts
以下ののコードでは、2つのオブジェクトを比較する気の利いた関数も残しています。私はそれを使用して、将来のルート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;
}
}
動作
この実装は、ユーザーがルーターで1回だけ訪れるすべての一意のルートを格納します。これは、サイトでのユーザーのセッション全体を通じて、メモリに格納されているコンポーネントに追加され続けます。保存するルートを制限したい場合、それを行う場所はshouldDetach
メソッドです。保存するルートを制御します。
例
ユーザーがホームページから何かを検索するとします。これにより、ユーザーは次のsearch/:term
ようなパスに移動しますwww.yourwebsite.com/search/thingsearchedfor
。検索ページには一連の検索結果が含まれています。彼らが戻ってきたくなった場合に備えて、あなたはこのルートを保存したいと思います!検索結果をクリックするとview/:resultId
、保存したくないに移動します。表示されるのはおそらく1回だけなので、上記の実装が整ったら、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はルートのインスタンスを1つしか保存しないので、このストレージは軽量になりsearch/:term
、他のすべてではなく、にあるコンポーネントのみを保存します!
追加リンク
まだ多くのドキュメントはありませんが、存在するものへのリンクをいくつか示します。
Angular Docs: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