I implemented RouteReuseStrategy
suggested here and also updated a bit, because on shouldAttach
the routeConfig.path
was empty, and the handler was not cached. I have @angular/router: 3.4.7
.
import {RouteReuseStrategy, DetachedRouteHandle, ActivatedRouteSnapshot} from "@angular/router"
export class CustomReuseStrategy implements RouteReuseStrategy {
handlers: {[key: string]: DetachedRouteHandle} = {};
currentPath: string = '';
shouldDetach(route: ActivatedRouteSnapshot): boolean {
return true
}
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
this.handlers[route.routeConfig.path] = handle
}
shouldAttach(route: ActivatedRouteSnapshot): boolean {
// todo route.routeConfig.path was empty
if (!!this.currentPath && !!this.handlers[this.currentPath]) {
route.routeConfig.path = this.currentPath;
return true
} else {
return false
}
}
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
if (!route.routeConfig) return null;
return this.handlers[route.routeConfig.path]
}
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
this.currentPath = curr.routeConfig && curr.routeConfig.path;
return future.routeConfig === curr.routeConfig
}
deleteHandler(handler:string) {
this.handlers[handler] && delete this.handlers[handler]
}
}
Everything is working, except when I try to navigate back the 3rd time to a route I got the following error:
EXCEPTION: Uncaught (in promise): Error: Cannot reattach ActivatedRouteSnapshot with a different number of children
Error: Cannot reattach ActivatedRouteSnapshot with a different number of children
at setFutureSnapshotsOfActivatedRoutes (http://localhost:4200/js/vendor.bundle.js:93110:15) [angular]
at createNode (http://localhost:4200/js/vendor.bundle.js:93091:9) [angular]
at http://localhost:4200/js/vendor.bundle.js:93131:16 [angular]
at Array.map (native) [angular]
at createOrReuseChildren (http://localhost:4200/js/vendor.bundle.js:93124:26) [angular]
at createNode (http://localhost:4200/js/vendor.bundle.js:93086:41) [angular]
at http://localhost:4200/js/vendor.bundle.js:93128:24 [angular]
at Array.map (native) [angular]
at createOrReuseChildren (http://localhost:4200/js/vendor.bundle.js:93124:26) [angular]
at createNode (http://localhost:4200/js/vendor.bundle.js:93086:41) [angular]
at createRouterState (http://localhost:4200/js/vendor.bundle.js:93072:33) [angular]
at MapSubscriber.project (http://localhost:4200/js/vendor.bundle.js:26366:153) [angular]
at MapSubscriber._next (http://localhost:4200/js/vendor.bundle.js:15890:35) [angular]
at MapSubscriber.Subscriber.next (http://localhost:4200/js/vendor.bundle.js:4861:18) [angular]
at MergeMapSubscriber.notifyNext (http://localhost:4200/js/vendor.bundle.js:19133:30) [angular]
at InnerSubscriber._next (http://localhost:4200/js/vendor.bundle.js:106234:21) [angular]
at InnerSubscriber.Subscriber.next (http://localhost:4200/js/vendor.bundle.js:4861:18) [angular]
at MapSubscriber._next (http://localhost:4200/js/vendor.bundle.js:15896:26) [angular]
at MapSubscriber.Subscriber.next (http://localhost:4200/js/vendor.bundle.js:4861:18) [angular]
at ReduceSubscriber._complete (http://localhost:4200/js/vendor.bundle.js:36808:30) [angular]
at ReduceSubscriber.Subscriber.complete (http://localhost:4200/js/vendor.bundle.js:4886:18) [angular]
at MergeMapSubscriber._complete (http://localhost:4200/js/vendor.bundle.js:19125:30) [angular]
at MergeMapSubscriber.Subscriber.complete (http://localhost:4200/js/vendor.bundle.js:4886:18) [angular]
at ArrayObservable._subscribe (http://localhost:4200/js/vendor.bundle.js:12465:24) [angular]
at ArrayObservable.Observable._trySubscribe (http://localhost:4200/js/vendor.bundle.js:221:25) [angular]
at ArrayObservable.Observable.subscribe (http://localhost:4200/js/vendor.bundle.js:209:27) [angular]
at MergeMapOperator.call (http://localhost:4200/js/vendor.bundle.js:19075:23) [angular]
at Observable.subscribe (http://localhost:4200/js/vendor.bundle.js:206:22) [angular]
at ReduceOperator.call (http://localhost:4200/js/vendor.bundle.js:36763:23) [angular]
at Observable.subscribe (http://localhost:4200/js/vendor.bundle.js:206:22) [angular]
at MapOperator.call (http://localhost:4200/js/vendor.bundle.js:15867:23) [angular]
at Observable.subscribe (http://localhost:4200/js/vendor.bundle.js:206:22) [angular]
at Object.subscribeToResult (http://localhost:4200/js/vendor.bundle.js:5096:27) [angular]
...
I have same problem and was trying for last one week. Seems like it works fine for me now. Main challenge is defining a unique key for any given routes. I find RouteReuseStrategy become bit complex to understand and define key for each route when your configure you app with child routing and lazy loading. I try to explain my understanding and I hope this information helpful.
RouteReuseStrategy
I was trying to figure out the RouteReuseStrategy method calls and parameter details to generate unique key and come up with following diagram by debugging my application. Noticed sometimes stage parameters passed to method calls and try to ignore those parameters in RouteReuseStrategy implementation.
CustomReuseStrategy
RouteReuseStrategy Implementation:
/* tslint:disable */
import {ActivatedRouteSnapshot, DetachedRouteHandle, Params, RouteReuseStrategy} from "@angular/router";
interface RouteStorageObject {
snapshot: ActivatedRouteSnapshot;
handle: DetachedRouteHandle;
}
export class CustomReuseStrategy implements RouteReuseStrategy {
storedRoutes: { [key: string]: RouteStorageObject } = {};
getFurthestDecendantParams(route: ActivatedRouteSnapshot, sum: any): ActivatedRouteSnapshot {
if (route.children.length > 0) {
let child: ActivatedRouteSnapshot = route.children[0];
sum.sum = sum.sum + this.sumParams(child.params);
return this.getFurthestDecendantParams(child, sum);
}
return route;
}
sumParams(params: Params): string {
return Object.keys(params).reduce((param, key) => {
return param + params[key];
}, "");
}
calcKey(route: ActivatedRouteSnapshot) {
let paramKey = {
sum: ""
}
if (route.children.length > 0) {
this.getFurthestDecendantParams(route, paramKey).params;
} else {
paramKey.sum = this.sumParams(route.params);
}
if (paramKey.sum != "") {
paramKey.sum = "_" + paramKey.sum;
}
return route.data.key + paramKey.sum;
}
public shouldDetach(route: ActivatedRouteSnapshot): boolean {
console.info("CustomReuseStrategy.shouldDetach() - route key: " + this.calcKey(route));
return !("product" === this.calcKey(route));
}
public store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
let storedRoute: RouteStorageObject = {
snapshot: route,
handle: handle
};
console.info("CustomReuseStrategy.store() - route key: " + this.calcKey(route));
this.storedRoutes[this.calcKey(route)] = storedRoute;
}
public shouldAttach(route: ActivatedRouteSnapshot): boolean {
console.info("CustomReuseStrategy.shouldAttach() - route key: " + this.calcKey(route));
return this.storedRoutes[this.calcKey(route)] !== undefined;
}
public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
console.info("CustomReuseStrategy.retrieve() - route key: " + this.calcKey(route));
if (this.storedRoutes[this.calcKey(route)] === undefined) {
/* Just return undefined */
return null;
} else {
return this.storedRoutes[this.calcKey(route)].handle;
}
}
public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
let returnValue = (future.routeConfig === curr.routeConfig);
if (future.routeConfig != null && curr.routeConfig != null) {
returnValue = this.calcKey(future) === this.calcKey(curr);
console.info("CustomReuseStrategy.shouldReuseRoute() - future: " + this.calcKey(future) + ", curr: " + this.calcKey(curr) +
", future.routeConfig.path:" + future.routeConfig.path + ", curr.routeConfig.path:" + curr.routeConfig.path + ", returnValue: " + returnValue);
} else {
console.info("CustomReuseStrategy.shouldReuseRoute() - future: " + this.calcKey(future) + ", curr: " + this.calcKey(curr) +
", future.routeConfig:" + future.routeConfig + ", curr.routeConfig:" + curr.routeConfig + ", returnValue: " + returnValue);
}
return returnValue;
}
}
In Summary
Following things define in my app and RouteReuseStrategy works fine.
- Defined routes with data key and children lazy loading
- Only single route-outlet
- Unique key for each routes
- Angular Version 2.4.10 and Router Version 3.4.10
Reference
来源:https://stackoverflow.com/questions/42643748/angular-2-implementing-routereusestrategy