My app uses a lazy loading tab system that is managed with a service. When a user selects an option on the nav menu, two things happen :
This works fine but I need something more complex :
RouteReuseStrategy
is best suited for this kind of behaviour.For now, the only solution that I found is to store the state of each component in session storage and update the components state when clicking on the tab. To achieve this, my components need to subscribe to NavigationStart
and NavigationEnd
events.
This works fine for fairly simple components but for some very complex components containing dozens of buttons, form etc. It can quickly become a nightmare to manage since I have to write the loading and saving logic for each component. Session storage being limited in size, this is also a limit of this method.
Any suggestion would be very much appeciated.
For anyone running into the same issue, the sticky state can be achieved by implementing RouteReuseStrategy
. You can find an example here.
I thought that this approach would not work with multiple instances of the same component but it actually does. You just need to tweak the implementation a little bit :
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
in the constuctor of your component so a new component is loaded even if the route doesn't change.CacheRouteReuseStrategy
@Injectable()
export class CacheRouteReuseStrategy implements RouteReuseStrategy {
constructor(private linkService : LinkService) {
}
shouldDetach(route: ActivatedRouteSnapshot): boolean {
return true;
}
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
// If previous route and active route are the same, handle is null.
// In that case, we don't want to update the store (we don't want to set a null object for the route and loose informations)
if (handle != null && this.linkService.previousLink != null){
this.linkService.storedRoutes.set(route.routeConfig?.path + this.linkService.previousLink.tabId, handle);
}
}
shouldAttach(route: ActivatedRouteSnapshot): boolean {
if (this.linkService.activeLink != null) {
return !!route.routeConfig && !!this.linkService.storedRoutes.get(<string>route.routeConfig.path + this.linkService.activeLink.tabId);
} else {
return false;
}
}
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
// @ts-ignore
return this.linkService.storedRoutes.get(route.routeConfig.path + this.linkService.activeLink.tabId);
}
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return future.routeConfig === curr.routeConfig;
}
}
I have a service called LinkService to keep track of the route I'm leaving (previousLink) and the route I'm trying to accesss (activeLink). previousLink is the object I want to add to the store when leaving a route and activeLink is the object I want to get from the store when accessing a route.
@Injectable({
providedIn: 'root'
})
export class LinkService {
links: LinkModel[];
activeLink: LinkModel;
previousLink: LinkModel;
storedRoutes = new Map<string, DetachedRouteHandle>();
}
export interface LinkModel {
route: string,
parameters: string | null,
tabId: string,
tabTitle: string
}
These informations are updated by another service, called TabService. What it does is pretty straightforward (create, change and remove tab logic) but I can post it later if anyone needs it.