Angular 9. Redémarrez les gardes de page en cours. Déclenchez les gardes d'itinéraire actuels

Face à la nécessité de redémarrer les gardes pour la page en cours, quelle que soit la page ouverte.



Je n'ai pas trouvé de solution standard, et celles proposées sur Internet sont limitées à une page. Par conséquent, j'ai écrit le mien et j'ai décidé de le partager.



Description du cas



Les pages d'application sont divisées en 3 groupes:



  • Pour les utilisateurs autorisĂ©s uniquement

  • Pour les utilisateurs non autorisĂ©s uniquement

  • Pour tous les utilisateurs



Vous pouvez vous connecter ou vous déconnecter sur n'importe quelle page.



Si vous entrez / sortez sur une page à accÚs limité, vous devez accéder à la page autorisée.



Si la page n'a pas de restrictions, vous devez rester sur la page actuelle.



Pour une meilleure compréhension, il est conseillé de connaßtre:



  • Garde - CanActivate

  • Route

  • Routeur

  • ActivatedRoute

  • Sortie de routeur



DĂ©cision



La différenciation des droits d'accÚs aux pages s'effectue via guard - CanActivate. Le contrÎle est effectué lorsque vous accédez à la page, mais ne réagit pas aux changements de droits lorsque la transition a déjà été effectuée. Pour éviter la duplication de la logique des droits d'accÚs, nous redémarrons de force les gardes.



Le redémarrage des gardes pour la page actuelle se fait en naviguant dans l'url actuelle. Et les modifications apportées aux stratégies Router.onSameUrlNavigation et Route.runGuardsAndResolvers.



Voici une solution toute faite. Plus de détails dans la section suivante.
  import { Injectable } from '@angular/core';
  import { ActivatedRoute, PRIMARY_OUTLET, Router, RunGuardsAndResolvers } from '@angular/router';

  @Injectable()
  export class GuardControlService {
    constructor(
      private route: ActivatedRoute,
      private router: Router,
    ) {}

    /**
    *   guard-  url
    */
    forceRunCurrentGuards(): void {
      //   Router.onSameUrlNavigation       url
      const restoreSameUrl = this.changeSameUrlStrategy(this.router, 'reload');

      //   ActivatedRoute  primary outlet
      const primaryRoute: ActivatedRoute = this.getLastRouteForOutlet(this.route.root, PRIMARY_OUTLET);

      //   runGuardsAndResolvers  ActivatedRoute          url
      const restoreRunGuards = this.changeRunGuardStrategies(primaryRoute, 'always');

      //   
      this.router.navigateByUrl(
        this.router.url
      ).then(() => {
        //  onSameUrlNavigation
        restoreSameUrl();
        //  runGuardsAndResolvers
        restoreRunGuards();
      });
    }

    /**
    *  onSameUrlNavigation    
    * @param router - Router,    
    * @param strategy -  
    * @return callback   
    */
    private changeSameUrlStrategy(router: Router, strategy: 'reload' | 'ignore'): () => void {
      const onSameUrlNavigation = router.onSameUrlNavigation;
      router.onSameUrlNavigation = strategy;

      return () => {
        router.onSameUrlNavigation = onSameUrlNavigation;
      }
    }

    /**
    *   route  outlet-
    * @param route - Route    
    * @param outlet -  outlet-,    
    * @return  ActivatedRoute   outlet
    */
    private getLastRouteForOutlet(route: ActivatedRoute, outlet: string): ActivatedRoute {
      if (route.children?.length) {
        return this.getLastRouteForOutlet(
          route.children.find(item => item.outlet === outlet),
          outlet
        );
      } else {
        return route;
      }
    }

    /**
    *  runGuardsAndResolvers  ActivatedRoute   ,    
    * @param route - ActivatedRoute    
    * @param strategy -  
    * @return callback   
    */
    private changeRunGuardStrategies(route: ActivatedRoute, strategy: RunGuardsAndResolvers): () => void {
      const routeConfigs = route.pathFromRoot
        .map(item => {
          if (item.routeConfig) {
            const runGuardsAndResolvers = item.routeConfig.runGuardsAndResolvers;
            item.routeConfig.runGuardsAndResolvers = strategy;
              return runGuardsAndResolvers;
              } else {
            return null;
          }
        });

      return () => {
        route.pathFromRoot
          .forEach((item, index) => {
            if (item.routeConfig) {
              item.routeConfig.runGuardsAndResolvers = routeConfigs[index];
            }
          });
      }
    }
  }
  




Description supplémentaire de la solution



La premiÚre chose que vous voulez essayer pour redémarrer les gardes est d'utiliser la navigation vers l'url actuelle.



this.router.navigateByUrl(this.router.url);


Mais, par défaut, l'événement de transition d'url actuel est ignoré et rien ne se passe. Pour que cela fonctionne, vous devez configurer le routage.



Configurer le routage



1. Changez la stratégie du routeur. onSameUrlNavigation



onSameUrlNavigation peut prendre les valeurs suivantes:



onSameUrlNavigation: 'reload' | 'ignore';


Pour la sensibilité à la transition vers l'url actuelle, vous devez définir «recharger».



Le changement de stratĂ©gie ne se recharge pas, mais gĂ©nĂšre un Ă©vĂ©nement de navigation supplĂ©mentaire. Il peut ĂȘtre obtenu via un abonnement:



this.router.events.subscribe();


2. Changez la stratégie d'itinéraire. runGuardsAndResolvers



runGuardsAndResolvers peut prendre les valeurs suivantes:



type RunGuardsAndResolvers = 'pathParamsChange' | 'pathParamsOrQueryParamsChange' | 'paramsChange' | 'paramsOrQueryParamsChange' | 'always' | ((from: ActivatedRouteSnapshot, to: ActivatedRouteSnapshot) => boolean);


Pour la sensibilité à la transition vers l'URL actuelle, vous devez définir «toujours».



Configuration du routage pendant la configuration de l'application



onSameUrlNavigation:



const routes: : Route[] = [];
@NgModule({
  imports: [
    RouterModule.forRoot(
      routes,
      { onSameUrlNavigation: 'reload' }
    )
  ]
})


runGuardsAndResolvers:



const routes: Route[] = [
  {
    path: '',
    component: AppComponent,
    runGuardsAndResolvers: 'always',
  }
];


Configuration du routage au moment de l'exécution



constructor(
  private router: Router,
  private route: ActivatedRoute
) {
  this.router.onSameUrlNavigation = 'reload';
  this.route.routeConfig.runGuardsAndResolvers = 'always';
}


Redémarrage des gardes



Pour redémarrer les gardes d'une page spécifique, il suffit de configurer le routage lors de la configuration.



Mais pour recharger les gardes de n'importe quelle page, changer runGuardsAndResolvers dans chaque Route entraßnera des vérifications inutiles. Et la nécessité de toujours se souvenir de ce paramÚtre - aux erreurs.



Étant donnĂ© que notre cas suppose un redĂ©marrage pour n'importe quelle page sans restrictions dans la configuration de l'application, vous avez besoin de:



1. Remplacez onSameUrlNavigation et conservez la valeur actuelle



//   Router.onSameUrlNavigation       url
const restoreSameUrl = this.changeSameUrlStrategy(this.router, 'reload');

...
/**
*  onSameUrlNavigation    
* @param router - Router,    
* @param strategy -  
* @return callback   
*/
private changeSameUrlStrategy(router: Router, strategy: 'reload' | 'ignore'): () => void {
  const onSameUrlNavigation = router.onSameUrlNavigation;
  router.onSameUrlNavigation = strategy;

  return () => {
    router.onSameUrlNavigation = onSameUrlNavigation;
  }
}


2. Obtenez ActivatedRoute pour l'url actuelle



Étant donnĂ© que l'injection ActivatedRoute est effectuĂ©e dans le service, l'ActivatedRoute rĂ©sultant n'est pas associĂ© Ă  l'url actuelle.



ActivatedRoute pour l'URL actuelle se trouve dans la derniĂšre prise principale et vous devez la trouver:



//   ActivatedRoute  primary outlet
const primaryRoute: ActivatedRoute = this.getLastRouteForOutlet(this.route.root, PRIMARY_OUTLET);

...
/**
*   route  outlet-
* @param route - Route    
* @param outlet -  outlet-,    
* @return  ActivatedRoute   outlet
*/
private getLastRouteForOutlet(route: ActivatedRoute, outlet: string): ActivatedRoute {
  if (route.children?.length) {
    return this.getLastRouteForOutlet(
      route.children.find(item => item.outlet === outlet),
      outlet
   );
  } else {
    return route;
  }
}


3. Remplacez runGuardsAndResolvers pour tous les ActivatedRoute et ses ancĂȘtres, en conservant les valeurs actuelles



Un garde limitant l'accĂšs peut ĂȘtre situĂ© dans l'un des ancĂȘtres de l'actuel ActivatedRoute. Tous les ancĂȘtres sont situĂ©s dans pathFromRoot.



//   runGuardsAndResolvers  ActivatedRoute          url
const restoreRunGuards = this.changeRunGuardStrategies(primaryRoute, 'always');

...
/**
*  runGuardsAndResolvers  ActivatedRoute   ,    
* @param route - ActivatedRoute    
* @param strategy -  
* @return callback   
*/
private changeRunGuardStrategies(route: ActivatedRoute, strategy: RunGuardsAndResolvers): () => void {
  const routeConfigs = route.pathFromRoot
    .map(item => {
      if (item.routeConfig) {
        const runGuardsAndResolvers = item.routeConfig.runGuardsAndResolvers;
        item.routeConfig.runGuardsAndResolvers = strategy;
        return runGuardsAndResolvers;
      } else {
        return null;
      }
    });

  return () => {
    route.pathFromRoot
      .forEach((item, index) => {
        if (item.routeConfig) {
          item.routeConfig.runGuardsAndResolvers = routeConfigs[index];
        }
      });
  }
}


4. Accédez à l'url actuelle



this.router.navigateByUrl(this.router.url);


5. Remettez runGuardsAndResolvers et onSameUrlNavigation Ă  leur Ă©tat d'origine



restoreRunGuards();
restoreSameUrl();


6. Combinez les Ă©tapes en une seule fonction



constructor(
  private route: ActivatedRoute,
  private router: Router,
) {}

/**
*   guard-  url
*/
forceRunCurrentGuards(): void {
  //   Router.onSameUrlNavigation       url
  const restoreSameUrl = this.changeSameUrlStrategy(this.router, 'reload');

  //   ActivatedRoute  primary outlet
  const primaryRoute: ActivatedRoute = this.getLastRouteForOutlet(this.route.root, PRIMARY_OUTLET);

  //   runGuardsAndResolvers  ActivatedRoute          url
  const restoreRunGuards = this.changeRunGuardStrategies(primaryRoute, 'always');

  //   
  this.router.navigateByUrl(
    this.router.url
  ).then(() => {
    //  onSameUrlNavigation
    restoreSameUrl();
    //  runGuardsAndResolvers
    restoreRunGuards();
  });
}





J'espÚre que cet article vous a été utile. S'il y a d'autres solutions, je serai heureux de les voir dans les commentaires.



All Articles