Hey Anders, harika soru!
Sizinle neredeyse aynı kullanım durumum var ve aynı şeyi yapmak istedim! Kullanıcı arama> sonuçları al> Kullanıcı sonuca gider> Kullanıcı geri döner> BOOM hızlı bir şekilde sonuçlara geri döner , ancak kullanıcının gittiği belirli sonucu saklamak istemezsiniz.
tl; Dr.
RouteReuseStrategy
Stratejinizi uygulayan ve sağlayan bir sınıfa sahip olmanız gerekir ngModule
. Rota kaydedildiğinde değişiklik yapmak istiyorsanız, shouldDetach
işlevi değiştirin . Döndüğünde true
, Angular rotayı kaydeder. Rota eklendiğinde değişiklik yapmak istiyorsanız, shouldAttach
işlevi değiştirin . Ne zaman shouldAttach
true döndürür, Açısal istenen rotanın yerde saklanır rotayı kullanacaktır. İşte oynayabileceğiniz bir Plunker .
RouteReuseStrategy Hakkında
Bu soruyu sorduğunuzda, RouteReuseStrategy'nin Angular'a bir bileşeni yok etmemesini söylemenize , aslında onu daha sonraki bir tarihte yeniden oluşturmak için kaydetmenize izin verdiğini zaten anlıyorsunuz . Bu harika çünkü şunları sağlar:
- Azalan sunucu çağrıları
- Artan hız
- VE bileşen varsayılan olarak bırakıldığı durumda işler.
Sonuncusu, örneğin, kullanıcı çok fazla metin girmiş olsa bile geçici olarak bir sayfadan ayrılmak istiyorsanız önemlidir . Kurumsal uygulamalar, aşırı sayıda form nedeniyle bu özelliğe bayılacak !
Sorunu çözmek için bulduğum şey bu. Söylediğiniz gibi, RouteReuseStrategy
3.4.1 ve üzeri sürümlerde @ angular / router tarafından sunulanlardan yararlanmanız gerekiyor.
YAPMAK
İlk olarak projenizin @ angular / router sürüm 3.4.1 veya daha yüksek olduğundan emin olun.
Ardından , uygulayan sınıfınızı barındıracak bir dosya oluşturun RouteReuseStrategy
. Benimkini aradım reuse-strategy.ts
ve /app
saklamak için klasöre yerleştirdim . Şimdilik bu sınıf şöyle görünmeli:
import { RouteReuseStrategy } from '@angular/router';
export class CustomReuseStrategy implements RouteReuseStrategy {
}
(TypeScript hatalarınız için endişelenmeyin, her şeyi çözmek üzereyiz)
Sınıfınızı sizin için sağlayarak temel çalışmalarını tamamlayınapp.module
. Henüz yazmadım unutmayın CustomReuseStrategy
, ama devam ve gitmeli import
gelen o reuse-strategy.ts
hepsi aynı. Ayrıcaimport { RouteReuseStrategy } from '@angular/router';
@NgModule({
[...],
providers: [
{provide: RouteReuseStrategy, useClass: CustomReuseStrategy}
]
)}
export class AppModule {
}
Son parça , rotaların ayrılıp ayrılmayacağını, saklanıp saklanmayacağını, geri alınacağını ve yeniden bağlanıp bağlanmayacağını kontrol edecek sınıfı yazmaktır. Eski kopyala / yapıştır işlemine geçmeden önce , anladığım kadarıyla burada mekaniğin kısa bir açıklamasını yapacağım. Açıkladığım yöntemler için aşağıdaki kodu referans alın ve elbette kodda çok sayıda belge var .
- Gezinirken
shouldReuseRoute
ateşler. Bu bana biraz tuhaf geliyor, ancak geri dönerse true
, o zaman aslında şu anda bulunduğunuz rotayı yeniden kullanıyor ve diğer yöntemlerden hiçbiri ateşlenmiyor. Kullanıcı uzaklaşıyorsa sadece yanlış döndürürüm.
- Eğer
shouldReuseRoute
döner false
, shouldDetach
yangınlar. shouldDetach
rotayı saklamak isteyip istemediğinizi belirler ve bunu boolean
kadar gösteren bir değer döndürür . Yolları saklamaya / saklamamaya karar vermeniz gereken yer burasıdır , buna karşı depolanmasını istediğiniz bir dizi yolu kontrol ederek ve dizide route.routeConfig.path
yoksa yanlış döndürerek path
yapacağım.
- Geri
shouldDetach
dönülürse true
, store
ateşlenir, bu, rota hakkında istediğiniz bilgileri kaydetmeniz için bir fırsattır. Ne yaparsanız yapın, saklamanız gerekecek DetachedRouteHandle
çünkü Angular daha sonra depolanan bileşeninizi tanımlamak için bunu kullanır. Aşağıda, hem depolamak DetachedRouteHandle
ve ActivatedRouteSnapshot
dersime değişken yerel içine.
Demek ki gezinme hakkında depolama için mantığı, ama ne gördüm için bir bileşeni? Angular, navigasyonunuzu durdurmaya ve kayıtlı olanı yerine koymaya nasıl karar veriyor?
- Yine, sonra
shouldReuseRoute
geri döndü false
, shouldAttach
sen yeniden veya bellekte bileşeni kullanmak isteyip anlamaya şans olan ishal. Depolanan bir bileşeni yeniden kullanmak istiyorsanız, geri dönün true
ve yolunuza devam edin!
- Bunu bileşenin dönerek gösterecektir olan "bize kullanmak istiyorsunuz hangi bileşenin?" Soracaktır Şimdi Açısal
DetachedRouteHandle
dan retrieve
.
İhtiyacınız olan mantık hemen hemen bu! Aşağıdaki kodda reuse-strategy.ts
, size iki nesneyi karşılaştıracak şık bir işlev de bıraktım. Gelecekteki rotaları route.params
ve route.queryParams
kayıtlı olanları karşılaştırmak için kullanıyorum . Bunların hepsi eşleşirse, yeni bir tane oluşturmak yerine depolanan bileşeni kullanmak istiyorum. Ama bunu nasıl yapacağınız size kalmış!
yeniden-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;
}
}
davranış
Bu uygulama, kullanıcının yönlendiricide tam olarak bir kez ziyaret ettiği her benzersiz yolu depolar. Bu, kullanıcının sitedeki oturumu boyunca bellekte depolanan bileşenlere eklenmeye devam edecektir. Sakladığınız rotaları sınırlandırmak istiyorsanız, bunun yapılacağı yer shouldDetach
yöntemdir. Hangi rotaları kaydettiğinizi kontrol eder.
Misal
Kullanıcınızın ana sayfadan bir şey aradığını ve onu yola yönlendiren search/:term
ve şöyle görünebileceğini varsayalım www.yourwebsite.com/search/thingsearchedfor
. Arama sayfası bir grup arama sonucu içerir. Geri gelmek istemeleri ihtimaline karşı bu rotayı saklamak istiyorsunuz! Şimdi bir arama sonucunu tıklayın ve navigasyon olsun view/:resultId
hangi, yok onlar muhtemelen sadece bir kez orada olacağım gibi görerek, mağazaya istiyorum. Yukarıdaki uygulamayla, basitçe shouldDetach
yöntemi değiştirirdim ! Şöyle görünebilir:
Öncelikle saklamak istediğimiz bir dizi yol yapalım.
private acceptedRoutes: string[] = ["search/:term"];
şimdi, dizimize karşı shouldDetach
kontrol edebiliriz 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 bir rotanın yalnızca bir örneğini depolayacağından , bu depolama hafif olacaktır ve yalnızca konumunda bulunan bileşeni depolayacağız search/:term
, diğerlerini değil!
Ek Bağlantılar
Henüz çok fazla belge olmasa da, burada var olanlara birkaç bağlantı var:
Angular Docs: https://angular.io/docs/ts/latest/api/router/index/RouteReuseStrategy-class.html
Giriş Makalesi: https://www.softwarearchitekt.at/post/2016/12/02/sticky-routes-in-angular-2-3-with-routereusestrategy.aspx
nativescript-angular'ın varsayılan RouteReuseStrategy Uygulaması : https://github.com/NativeScript/nativescript-angular/blob/cb4fd3a/nativescript-angular/router/ns-route-reuse-strategy.ts