Information about my app (Angular 12):
- consists of 3 Modules, each has a overview page with a list and some detail pages
- every route has an area tag so I know in which module the user is navigating
So I wanted to implement Angular´s RouteReuseStrategy for the following behaviour:
Whenever a user navigates from list -> detail page and uses back button, the list component should be reused (detect back button trigger)
Whenever a user navigates to the list component from a different module / area the list component should not be reused
Whenever a user navigates from detail to another detail page, the detail component should be reused (default behaviour?)
Whenever a user leaves a module, by navigating to another or logging out, the stored components should be cleared / destroyed
Current Situation:
I implemented a custom RouteReuseStrategy, which works and the list component is reused ✓
- Only the scroll position is not restored, but this needs to be checked seperately ✕
I wanted to check the areatag inside the route, but the ActivatedRouteSnapshots are empty ✕
Detecting the backbutton press, but events fire to often and it breaks if I implement a basic back flag ✕
What is missing?
Detect backbutton navigation and modify the reuse of the component
Detect which module the route is part of to modify reuse or clean up the stored components
Code:
Example route in Module A
{
path: 'lista',
component: ListAComponent,
data: {
title: 'List overview',
areaCategory: AreaCategory.A,
reuseRoute: true,
},
},
{
path: 'lista/:id',
component: DetailAComponent,
data: {
title: 'Detail',
areaCategory: AreaCategory.A,
reuseRoute: false,
},
},
Example route Module B
{
path: 'listb',
component: ListBComponent,
data: {
title: 'List overview',
areaCategory: AreaCategory.B,
reuseRoute: true,
},
},
{
path: 'listb/:id',
component: DetailBComponent,
data: {
title: 'Detail',
areaCategory: AreaCategory.B,
reuseRoute: false,
},
},
app.module.ts
providers: [
{
provide: RouteReuseStrategy,
useClass: CustomReuseRouteStrategy,
}
],
Should be fine here globally or do I need to move it to each of the 3 modules?
ReuseRouteStrategy
@Injectable()
export class CustomReuseRouteStrategy implements RouteReuseStrategy {
private handlers: { [key: string]: DetachedRouteHandle } = {};
// Detect Backbutton-navigation
back = false;
constructor(location: LocationStrategy) {
location.onPopState(() => {
this.back = true;
});
}
shouldDetach(route: ActivatedRouteSnapshot): boolean {
if (!route.routeConfig || route.routeConfig.loadChildren) {
return false;
}
// Check route.data.reuse whether this route should be re used or not
let shouldReuse = false;
if (
route.routeConfig.data &&
route.routeConfig.data.reuseRoute &&
typeof route.routeConfig.data.reuseRoute === 'boolean'
) {
shouldReuse = route.routeConfig.data.reuseRoute;
}
return shouldReuse;
}
store(route: ActivatedRouteSnapshot, handler: DetachedRouteHandle): void {
if (handler) {
this.handlers[this.getUrl(route)] = handler;
}
}
shouldAttach(route: ActivatedRouteSnapshot): boolean {
if (!this.back) {
return false;
}
return !!this.handlers[this.getUrl(route)];
}
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
if (!this.back || !route.routeConfig || route.routeConfig.loadChildren) {
return null;
}
//this.back = false; -> does not work fires to often
return this.handlers[this.getUrl(route)];
}
shouldReuseRoute(future: ActivatedRouteSnapshot, current: ActivatedRouteSnapshot): boolean {
/** We only want to reuse the route if the data of the route config contains a reuse true boolean */
let reUseUrl = false;
if (future.routeConfig && future.routeConfig.data && typeof future.routeConfig.data.reuseRoute === 'boolean') {
reUseUrl = future.routeConfig.data.reuseRoute;
}
//const defaultReuse = future.routeConfig === current.routeConfig; -> used for navigating to same component but routeConfigs are empty therefore always match?
return reUseUrl;
}
private getUrl(route: ActivatedRouteSnapshot): string {
if (route.routeConfig) {
const url = route.routeConfig.path;
return url;
}
}
clearHandles() {
for (const key in this.handlers) {
if (this.handlers[key]) {
this.destroyHandle(this.handlers[key]);
}
}
this.handlers = {};
}
private destroyHandle(handle: DetachedRouteHandle): void {
const componentRef: ComponentRef<any> = handle['componentRef'];
if (componentRef) {
componentRef.destroy();
}
}
}
I noticed _routerState inside the ActivatedRouteSnapshot holds the url, which could be used to differentiate the modules, but I´d rather check the areaCategory from the route data but weirdly, the future & current ActivatedRouteSnapshots in the shouldReuseRoute method are mostly empty
Also I am not sure about using internal values like _routerState, since I heard these are not fixed an can change at any time
Log of empty future & current snapshot (only url is useful)
Why is it AppComponent instead of Detail or ListAComponent? Maybe thats why the data is empty?
I need to get the correct route / component to access the areaCategory to implement the desired behavior.
As requested here is a simple stackblitz with my setup
If I missed something, please let me know, would really appreciate the help