Toutes les solutions mentionnées étaient en quelque sorte insuffisantes dans notre cas. Nous avons une petite application commerciale avec :
- Page d'introduction
- Page de connexion
- App (après la connexion)
Nos exigences :
- Modules chargés paresseux
- Itinéraires à plusieurs niveaux
- Stocker tous les états du routeur / composant en mémoire dans la section app
- Option pour utiliser la stratégie de réutilisation angulaire par défaut sur des routes spécifiques
- Destruction de tous les composants stockés en mémoire lors de la déconnexion.
Exemple simplifié de nos itinéraires :
const routes: Routes = [{
path: '',
children: [
{
path: '',
canActivate: [CanActivate],
loadChildren: () => import('./modules/dashboard/dashboard.module').then(module => module.DashboardModule)
},
{
path: 'companies',
canActivate: [CanActivate],
loadChildren: () => import('./modules/company/company.module').then(module => module.CompanyModule)
}
]
},
{
path: 'login',
loadChildren: () => import('./modules/login/login.module').then(module => module.LoginModule),
data: {
defaultReuseStrategy: true, // Ignore our custom route strategy
resetReuseStrategy: true // Logout redirect user to login and all data are destroyed
}
}];
Stratégie de réutilisation :
export class AppReuseStrategy implements RouteReuseStrategy {
private handles: Map<string, DetachedRouteHandle> = new Map();
// Asks if a snapshot from the current routing can be used for the future routing.
public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return future.routeConfig === curr.routeConfig;
}
// Asks if a snapshot for the current route already has been stored.
// Return true, if handles map contains the right snapshot and the router should re-attach this snapshot to the routing.
public shouldAttach(route: ActivatedRouteSnapshot): boolean {
if (this.shouldResetReuseStrategy(route)) {
this.deactivateAllHandles();
return false;
}
if (this.shouldIgnoreReuseStrategy(route)) {
return false;
}
return this.handles.has(this.getKey(route));
}
// Load the snapshot from storage. It's only called, if the shouldAttach-method returned true.
public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
return this.handles.get(this.getKey(route)) || null;
}
// Asks if the snapshot should be detached from the router.
// That means that the router will no longer handle this snapshot after it has been stored by calling the store-method.
public shouldDetach(route: ActivatedRouteSnapshot): boolean {
return !this.shouldIgnoreReuseStrategy(route);
}
// After the router has asked by using the shouldDetach-method and it returned true, the store-method is called (not immediately but some time later).
public store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle | null): void {
if (!handle) {
return;
}
this.handles.set(this.getKey(route), handle);
}
private shouldResetReuseStrategy(route: ActivatedRouteSnapshot): boolean {
let snapshot: ActivatedRouteSnapshot = route;
while (snapshot.children && snapshot.children.length) {
snapshot = snapshot.children[0];
}
return snapshot.data && snapshot.data.resetReuseStrategy;
}
private shouldIgnoreReuseStrategy(route: ActivatedRouteSnapshot): boolean {
return route.data && route.data.defaultReuseStrategy;
}
private deactivateAllHandles(): void {
this.handles.forEach((handle: DetachedRouteHandle) => this.destroyComponent(handle));
this.handles.clear();
}
private destroyComponent(handle: DetachedRouteHandle): void {
const componentRef: ComponentRef<any> = handle['componentRef'];
if (componentRef) {
componentRef.destroy();
}
}
private getKey(route: ActivatedRouteSnapshot): string {
return route.pathFromRoot
.map((snapshot: ActivatedRouteSnapshot) => snapshot.routeConfig ? snapshot.routeConfig.path : '')
.filter((path: string) => path.length > 0)
.join('');
}
}