143 votes

Comment mettre en œuvre la stratégie RouteReuseStrategy shouldDetach pour des routes spécifiques en Angular 2 ?

J'ai un module Angular 2 dans lequel j'ai implémenté le routage et je voudrais que les états soient stockés lors de la navigation.
L'utilisateur doit être en mesure de :

  1. rechercher des documents à l'aide d'une "formule de recherche".
  2. naviguer vers l'un des résultats
  3. retourner à 'searchresult' - sans communiquer avec le serveur

Cela est possible notamment grâce à RouteReuseStrategy .
La question est la suivante :
Comment mettre en œuvre le fait que le document ne doit pas être stocké ?

Donc l'état du chemin d'accès "documents" doit être stocké et l'état du chemin d'accès "documents/:id" ne doit PAS être stocké ?

4voto

hovado Points 947

Toutes les solutions mentionnées étaient en quelque sorte insuffisantes dans notre cas. Nous avons une petite application commerciale avec :

  1. Page d'introduction
  2. Page de connexion
  3. App (après la connexion)

Nos exigences :

  1. Modules chargés paresseux
  2. Itinéraires à plusieurs niveaux
  3. Stocker tous les états du routeur / composant en mémoire dans la section app
  4. Option pour utiliser la stratégie de réutilisation angulaire par défaut sur des routes spécifiques
  5. 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('');
    }
}

3voto

红兵伍 Points 31

Ce qui suit est une référence de travail ! https://www.cnblogs.com/lovesangel/p/7853364.html

import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router';

export class CustomReuseStrategy implements RouteReuseStrategy {

    public static handlers: { [key: string]: DetachedRouteHandle } = {}

    private static waitDelete: string

    public static deleteRouteSnapshot(name: string): void {
        if (CustomReuseStrategy.handlers[name]) {
            delete CustomReuseStrategy.handlers[name];
        } else {
            CustomReuseStrategy.waitDelete = name;
        }
    }

    public shouldDetach(route: ActivatedRouteSnapshot): boolean {
        return true;
    }

    public store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
        if (CustomReuseStrategy.waitDelete && CustomReuseStrategy.waitDelete == this.getRouteUrl(route)) {
            // 
            CustomReuseStrategy.waitDelete = null
            return;
        }
        CustomReuseStrategy.handlers[this.getRouteUrl(route)] = handle
    }

    public shouldAttach(route: ActivatedRouteSnapshot): boolean {
        return !!CustomReuseStrategy.handlers[this.getRouteUrl(route)]
    }

    /** nul */
    public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
        if (!route.routeConfig) {
            return null
        }

        return CustomReuseStrategy.handlers[this.getRouteUrl(route)]
    }

    public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
        return future.routeConfig === curr.routeConfig &&
            JSON.stringify(future.params) === JSON.stringify(curr.params);
    }

    private getRouteUrl(route: ActivatedRouteSnapshot) {
        return route['_routerState'].url.replace(/\//g, '_')
    }
}

1 votes

Attention, ceci utilise une variable interne _routerState .

0 votes

@DarkNeuron fait _routerState causent-ils des dommages ?

2 votes

Non, mais Google n'est pas obligé de conserver cette variable, puisqu'elle est utilisée en interne et n'est pas exposée dans l'API.

1voto

Stas Amasev Points 16

J'ai été confronté à ces problèmes en mettant en œuvre une stratégie de réutilisation des itinéraires personnalisés :

  1. Effectuer des opérations sur une route attachée/détachée : gestion des abonnements, nettoyage, etc ;
  2. Conserver uniquement l'état de la dernière route paramétrée : optimisation de la mémoire ;
  3. Réutilisez un composant, pas un état : gérez l'état avec un outil de gestion d'état.
  4. Erreur "Impossible de rattacher ActivatedRouteSnapshot créé à partir d'une route différente" ;

J'ai donc écrit une bibliothèque qui résout ces problèmes. La bibliothèque fournit un service et des décorateurs pour les crochets attachés/détachés et utilise les composants d'une route pour stocker les routes détachées, et non les chemins d'une route.

Ejemplo:

/* Usage with decorators */
@onAttach()
public onAttach(): void {
  // your code...
}

@onDetach()
public onDetach(): void {
  // your code...
}

/* Usage with a service */
public ngOnInit(): void {
  this.cacheRouteReuse
    .onAttach(HomeComponent) // or any route's component
    .subscribe(component => {
      // your code...
    });

  this.cacheRouteReuse
    .onDetach(HomeComponent) // or any route's component
    .subscribe(component => {
      // your code...
    });
}

La bibliothèque : https://www.npmjs.com/package/ng-cache-route-reuse

1 votes

Un simple lien vers votre propre bibliothèque ou tutoriel n'est pas une bonne réponse. Il est préférable d'établir un lien vers cette bibliothèque, d'expliquer pourquoi elle résout le problème, de fournir du code sur la façon de le faire et d'indiquer que vous l'avez écrite. Voir : Qu'est-ce qu'une "bonne" autopromotion ?

0voto

Shadab Faiz Points 189

Toutes les réponses ci-dessus sont excellentes, mais aucune d'entre elles ne fonctionnera correctement si vous avez un routeur de type "lazy-load" et que celui-ci est imbriqué.

Pour surmonter cela, shouldReuseRoute et le chemin pour comparer les routes doit être modifié :

Path A: abc/xyx/3
Path B: abc/rty/8

<abc>
 <router-outlet></router-outlet>
</abc>

/* If we move from pathA to pathB or vice versa, 
 * then  `routeConfig` will be same since we are routing within the same abc, 
 * but to store the handle properly, you should store fullPath as key.
*/

  shouldReuseRoute(
    future: ActivatedRouteSnapshot,
    curr: ActivatedRouteSnapshot
  ): boolean {

    return future.routeConfig === curr.routeConfig;
  }

  private getPathFromRoot(route: ActivatedRouteSnapshot) {
    return (route["_urlSegment"]["segments"] as UrlSegment[])
      .map((seg) => seg.path)
      .join("/");
  }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X