Routage > Jalon 5 : Gardes de route

Références

L'actualité

Librairie

L'information

Jalon 5 : Gardes de route

Pour le moment, tout utilisateur peut naviguer n'importe où dans l'application à tout moment . Ce n'est pas toujours la bonne chose à faire.
  • Peut-être que l'utilisateur n'est pas autorisé à accéder au composant cible.
  • Peut-être que l'utilisateur doit d'abord se connecter ( s'authentifier ).
  • Vous devriez peut-être récupérer des données avant d'afficher le composant cible.
  • Vous voudrez peut-être enregistrer les modifications en attente avant de quitter un composant.
  • Vous pouvez demander à l'utilisateur s'il est acceptable de supprimer les modifications en attente plutôt que de les enregistrer.
Vous ajoutez des gardes à la configuration de la route pour gérer ces scénarios.

La valeur de retour d'un garde contrôle le comportement du routeur:
  • S'il revient true, le processus de navigation se poursuit.
  • Si elle revient false, le processus de navigation s'arrête et l'utilisateur reste sur place.
  • S'il retourne un UrlTree, la navigation en cours est annulée et une nouvelle navigation est lancée vers le UrlTreeretour.
Remarque : Le gardien peut également demander au routeur de naviguer ailleurs, annulant ainsi la navigation en cours. En le faisant à l'intérieur d'un garde, ce dernier devrait revenir false;

Le gardien peut renvoyer sa réponse booléenne de manière synchrone. Mais dans de nombreux cas, le gardien ne peut pas produire de réponse de manière synchrone. Le gardien pourrait poser une question à l'utilisateur, enregistrer les modifications apportées au serveur ou récupérer de nouvelles données. Ce sont toutes des opérations asynchrones.

En conséquence, un agent de protection du routage peut renvoyer un Observable ‹boolean› ou un Promise ‹boolean› et le routeur attend que l'observable se résolve en trueou false.

Remarque: L'observable fourni au routeur doit également être complet. Si l'observable ne se termine pas, la navigation ne continuera pas.

Le routeur prend en charge plusieurs interfaces de garde:
  • CanActivateassurer la médiation de la navigation sur un itinéraire.
  • CanActivateChildassurer la médiation de la navigation sur un itinéraire enfant.
  • CanDeactivatepour écarter la navigation de l'itinéraire actuel.
  • Resolveeffectuer la récupération des données de la route avant son activation.
  • CanLoadassurer la navigation vers un module de fonctions chargé de manière asynchrone.
Vous pouvez avoir plusieurs gardes à chaque niveau d'une hiérarchie de routage. Le routeur vérifie d'abord les gardes CanDeactivateet CanActivateChild, de la route enfant la plus profonde au sommet. Ensuite, il vérifie les CanActivategardes du haut en bas de la route des enfants. Si le module de fonction est chargé de manière asynchrone, la CanLoadprotection est vérifiée avant le chargement du module. Si un garde retourne faux, les gardes en attente qui ne sont pas terminés seront annulés et la navigation entière sera annulée.

Il y a plusieurs exemples dans les sections suivantes.


CanActivate : nécessitant une authentification

Les applications limitent souvent l'accès à une zone de fonctionnalités en fonction de l'identité de l'utilisateur. Vous pouvez autoriser l'accès uniquement aux utilisateurs authentifiés ou aux utilisateurs dotés d'un rôle spécifique. Vous pouvez bloquer ou limiter l'accès jusqu'à l'activation du compte de l'utilisateur.

La CanActivategarde est l'outil pour gérer ces règles métier de navigation.

Ajouter un module de fonctionnalité admin

Dans cette section, vous allez étendre le centre de crise avec de nouvelles fonctionnalités administratives. Ces fonctionnalités ne sont pas encore définies. Mais vous pouvez commencer par ajouter un nouveau module de fonctionnalité nommé AdminModule.

Générez un admindossier avec un fichier de module de fonctions et un fichier de configuration de routage.


Ensuite, générez les composants de support.




La structure du fichier de fonctionnalités d'administration ressemble à ceci:




Le module de fonctions admin contient les éléments AdminComponentutilisés pour le routage dans le module de fonctions, une route de tableau de bord et deux composants non terminés pour gérer les crises et les héros.

src / app / admin / admin / admin.component.html

src / app / admin / admin-tableau de bord / admin-dashboard.component.html

src / app / admin / admin.module.ts

src / app / admin / gérer-crises / gérer-crises.component.html

src / app / admin / manage-heroes / manage-heroes.component.html

Bien que le tableau de bord de l'administrateur RouterLinkne contienne qu'une barre oblique relative sans segment d'URL supplémentaire, il est considéré comme une correspondance avec tout itinéraire situé dans la zone de fonctions de l'administrateur. Vous voulez que le Dashboardlien soit actif uniquement lorsque l'utilisateur visite cet itinéraire. L'ajout d'une liaison supplémentaire à DashboardrouterLink, marque le lien comme actif lorsque l'utilisateur navigue vers l'URL et non lors de la navigation vers l'un des itinéraires enfants.[routerLinkActiveOptions]="{ exact: true }".//admin


Itinéraire sans composant : regrouper des itinéraires sans composant

src / app / admin / admin-routing.module.ts (routage admin)

En regardant la route des enfants sous le AdminComponent, il y a un pathet une children propriété mais elle n'utilise pas un component. Vous n'avez pas commis d'erreur dans la configuration. Vous avez défini un itinéraire sans composant.

L'objectif est de regrouper les Crisis Centeritinéraires de gestion sous le adminchemin. Vous n'avez pas besoin d'un composant pour le faire. Un itinéraire sans composant facilite la protection des itinéraires enfants.

Ensuite, importez-le AdminModuledans app.module.tset ajoutez-le au importstableau pour enregistrer les routes d'administration.

src / app / app.module.ts (module d'administration)

Ajoutez un lien "Admin" au AppComponentshell afin que les utilisateurs puissent accéder à cette fonctionnalité.

src / app / app.component.html (modèle)

Garder la fonctionnalité admin

Actuellement, tous les itinéraires du centre de crise sont ouverts à tous. La nouvelle fonctionnalité admin devrait être accessible uniquement aux utilisateurs authentifiés.

Vous pouvez masquer le lien jusqu'à ce que l'utilisateur se connecte. Mais c'est difficile et difficile à maintenir.

Au lieu de cela, vous écrirez une canActivate()méthode de protection pour rediriger les utilisateurs anonymes vers la page de connexion lorsqu'ils essaieront d'entrer dans la zone d'administration.

Il s'agit d'une protection à usage général (vous pouvez imaginer d'autres fonctionnalités nécessitant des utilisateurs authentifiés) afin de générer un AuthGuarddans le authdossier.


Pour le moment, vous êtes intéressé par le fonctionnement des protecteurs. La première version ne sert donc à rien. Il se connecte simplement à console et à returnstrue immédiatement, permettant à la navigation de continuer:

src / app / auth / auth.guard.ts (extrait)

Ensuite, ouvrez admin-routing.module.ts, importez la AuthGuardclasse et mettez à jour la route d'administration avec une canActivatepropriété guard qui la référence:

src / app / admin / admin-routing.module.ts (route d'administration gardée)

La fonctionnalité admin est maintenant protégée par le gardien, bien que mal protégée.

Enseigner à AuthGuard l'authentification

Faites AuthGuardau moins prétendre vous authentifier.

Le AuthGuarddoit appeler un service d'application capable de connecter un utilisateur et de conserver des informations sur l'utilisateur actuel. Générez un nouveau AuthServicedans le authdossier:


Mettez AuthService à jour le pour vous connecter à l'utilisateur:

src / app / auth / auth.service.ts (extrait)

Bien qu'il ne se connecte pas réellement, il a tout ce dont vous avez besoin pour cette discussion. Il a un isLoggedIndrapeau pour vous dire si l'utilisateur est authentifié. Sa loginméthode simule un appel d'API à un service externe en renvoyant ne observable résolue après une courte pause. La redirectUrlpropriété stockera l'URL tentée afin que vous puissiez y accéder après l'authentification.

Révisez le AuthGuardpour l'appeler.

src / app / auth / auth.guard.ts (v2)

Notez que vous injectez le AuthServiceet le Routerdans le constructeur. Vous n'avez pas encore fourni le AuthService, mais il est bon de savoir que vous pouvez injecter des services utiles dans les gardes d'acheminement.

Cette garde renvoie un résultat booléen synchrone. Si l'utilisateur est connecté, il renvoie true et la navigation se poursuit.

Le ActivatedRouteSnapshotcontient le futur itinéraire qui sera activé et le RouterStateSnapshot contient le futur RouterState de l'application, si vous passez par le contrôle de garde.

Si l'utilisateur n'est pas connecté, vous stockez l'URL tentée dont il est issu RouterStateSnapshot.urlet indiquez au routeur qu'il doit accéder à une page de connexion, page que vous n'avez pas encore créée. Cette navigation secondaire annule automatiquement la navigation en cours. checkLogin()revient falsejuste pour être clair à ce sujet.


Ajouter le LoginComponent

Vous avez besoin d'un LoginComponentpour que l'utilisateur se connecte à l'application. Une fois connecté, vous allez rediriger vers l'URL stockée, le cas échéant, ou utiliser l'URL par défaut. Il n'y a rien de nouveau à propos de ce composant ou de la manière dont vous le connectez à la configuration du routeur.

Enregistrer un /loginitinéraire dans le auth/auth-routing.module.ts. Dans app.module.ts, importez et ajoutez le AuthModuleaux AppModuleimportations.

src / app / app.module.ts

src / app / auth / login / login.component.html

src / app / auth / login / login.component.ts


src / app / auth / auth.module.ts


CanActivateChild : protection des routes enfants

Vous pouvez également protéger les itinéraires enfants avec le CanActivateChildgardien. La CanActivateChildgarde est semblable à la CanActivategarde. La principale différence réside dans le fait qu'il s'exécute avant l'activation de tout itinéraire enfant.

Vous avez protégé le module de fonctionnalité admin contre tout accès non autorisé. Vous devez également protéger les itinéraires enfants au sein du module de fonctionnalité.

étendez le AuthGuardpour protéger lorsque vous naviguez entre les adminitinéraires. Ouvrez auth.guard.tset ajoutez l' CanActivateChildinterface aux jetons importés à partir du package de routeur.

Ensuite, implémentez la canActivateChild()méthode qui prend les mêmes arguments que la canActivate()méthode: an ActivatedRouteSnapshotet RouterStateSnapshot. La canActivateChild()méthode peut renvoyer un Observable ‹boolean› ou Promise ‹boolean› pour les contrôles async et un booleanpour les contrôles de synchronisation.

Celui-ci retourne un boolean :

src / app / auth / auth.guard.ts (extrait)

Ajoutez la même chose AuthGuardà la component-lessroute d'administration pour protéger toutes les autres routes enfants en même temps au lieu de les ajouter AuthGuardindividuellement à chaque route.

src / app / admin / admin-routing.module.ts (extrait)


CanDeactivate : gestion des modifications non enregistrées

De retour dans le flux de travail "Heroes", l'application accepte chaque changement de héros immédiatement, sans hésitation ni validation.

Dans le monde réel, vous devrez peut-être accumuler les modifications apportées par les utilisateurs. Vous devrez peut-être valider plusieurs champs. Vous devrez peut-être valider sur le serveur. Vous devrez peut-être conserver les modifications en attente jusqu'à ce que l'utilisateur les confirme en tant que groupe ou annule et annule toutes les modifications.

Que faites-vous des modifications non approuvées et non enregistrées lorsque l'utilisateur navigue au loin ? Vous ne pouvez pas simplement partir et risquer de perdre les modifications de l'utilisateur; ce serait une expérience terrible.

Il est préférable de faire une pause et de laisser l'utilisateur décider quoi faire. Si l'utilisateur annule, vous restez sur place et autorisez d'autres modifications. Si l'utilisateur approuve, l'application peut enregistrer.

Vous pouvez toujours retarder la navigation jusqu'à ce que la sauvegarde réussisse. Si vous laissez l'utilisateur passer immédiatement à l'écran suivant et que la sauvegarde échoue (les données sont peut-être considérées comme non valides), vous perdriez le contexte de l'erreur.

Vous ne pouvez pas bloquer en attendant le serveur, ce n'est pas possible dans un navigateur. Vous devez arrêter la navigation pendant que vous attendez, de manière asynchrone, que le serveur revienne avec sa réponse.

Tu as besoin de la CanDeactivategarde.

Annuler et sauvegarder

L'exemple d'application ne parle pas à un serveur. Heureusement, vous avez un autre moyen de démontrer un hook de routeur asynchrone.

Les utilisateurs mettent à jour les informations de crise dans le fichier CrisisDetailComponent. Contrairement à HeroDetailComponent, les modifications apportées par l'utilisateur ne mettent pas à jour l'entité de crise immédiatement. Au lieu de cela, l'application met à jour l'entité lorsque l'utilisateur appuie sur le bouton Enregistrer et annule les modifications lorsque l'utilisateur appuie sur le bouton Annuler.

Les deux boutons permettent de revenir à la liste de crise après l'enregistrement ou l'annulation.

src / app / centre de crise / crise-détail / crise-détail.component.ts (méthodes annuler et enregistrer)

Que se passe-t-il si l'utilisateur essaie de s'éloigner sans enregistrer ni annuler ? L'utilisateur peut appuyer sur le bouton retour du navigateur ou cliquer sur le lien des héros. Les deux actions déclenchent une navigation. L'application doit-elle être enregistrée ou annulée automatiquement ?

Cette démo ne fait pas non plus. Au lieu de cela, il demande à l'utilisateur de faire ce choix explicitement dans une boîte de dialogue de confirmation qui attend sa réponse de manière asynchrone.

Vous pouvez attendre la réponse de l'utilisateur avec un code de blocage synchrone. L'application sera plus réactive (et pourra effectuer d'autres tâches) en attendant la réponse de l'utilisateur de manière asynchrone. Attendre l'utilisateur de manière asynchrone revient à attendre que le serveur soit asynchrone.

Générez un Dialogservice pour gérer la confirmation de l'utilisateur.

Ajoutez une confirm()méthode à DialogServicepour inviter l'utilisateur à confirmer son intention. Il window.confirms'agit d'une action de blocage qui affiche une boîte de dialogue modale et attend l'interaction de l'utilisateur.
src / app / dialog.service.ts

Il renvoie un Observablequi est résolu lorsque l'utilisateur décide finalement quoi faire: soit ignorer les modifications et naviguer (true), soit conserver les modifications en attente et rester dans l'éditeur de crise (false).

Générez une garde qui vérifie la présence d'une méthode canDeactivate() dans un composant - n'importe quel composant.

Le CrisisDetailComponentaura cette méthode. Mais le gardien n'a pas à le savoir. Le gardien ne devrait pas connaître les détails de la méthode de désactivation d'un composant. Il suffit de détecter que le composant possède une méthode canDeactivate() et de l'appeler. Cette approche rend la garde réutilisable.

src / app / can-deactivate.guard.ts

Vous pouvez également créer une CanDeactivategarde spécifique au composant pour le CrisisDetailComponent. La canDeactivate()méthode vous fournit l'instance actuelle du component, le courant ActivatedRouteet RouterStateSnapshotau cas où vous auriez besoin d'accéder à des informations externes. Cela peut être utile si vous souhaitez uniquement utiliser cette protection pour ce composant et obtenir les propriétés du composant ou confirmer si le routeur doit autoriser la navigation.

src / app / can-deactivate.guard.ts (spécifique au composant)

En regardant en arrière CrisisDetailComponent, il implémente le flux de travail de confirmation pour les modifications non enregistrées.

src / app / centre de crise / crise-détail / crise-détail.component.ts (extrait)

Notez que la canDeactivate()méthode peut retourner de manière synchrone; il revient trueimmédiatement s'il n'y a pas de crise ou s'il n'y a pas de modifications en attente. Mais il peut également renvoyer un Promiseou un Observableet le routeur attendra que cela se résolve en vérité (navigation) ou en fausseté (restez sur place).

Ajoutez le Guardà la route de détail de crise en crisis-center-routing.module.tsutilisant la canDeactivatepropriété array.

src / app / centre de crise / centre de crise / routage.module.ts (peut désactiver la garde)

Vous avez maintenant donné à l'utilisateur une protection contre les modifications non enregistrées.


Résoudre : pré-récupérer les données du composant

Dans Hero Detailet Crisis Detail, l'application a attendu que la route soit activée pour aller chercher le héros ou la crise en question.

Cela a bien fonctionné, mais il existe un meilleur moyen. Si vous utilisiez une API du monde réel, il pourrait s'écouler un certain délai avant que les données à afficher soient renvoyées par le serveur. Vous ne voulez pas afficher un composant vierge en attendant les données.

Il est préférable de pré-extraire les données du serveur pour qu'il soit prêt au moment où la route est activée. Cela vous permet également de gérer les erreurs avant le routage vers le composant. Il ne idsert à rien de naviguer vers un détail de crise pour une personne sans dossier. Il serait préférable de renvoyer l'utilisateur à l'écran qui ne montre que les centres de crise valides. Crisis List

En résumé, vous souhaitez différer le rendu du composant routé jusqu'à ce que toutes les données nécessaires aient été extraites.

Vous avez besoin d'un résolveur.


Récupérer les données avant de naviguer dans le

Pour le moment, le CrisisDetailComponentrécupère la crise sélectionnée. Si la crise n’est pas trouvée, il retourne à la vue de la liste de crises.

L'expérience pourrait être meilleure si tout cela était traité en premier, avant que l'itinéraire ne soit activé. Un CrisisDetailResolverservice peut extraire Crisisou naviguer si il Crisis n’existe pas avant d’ activer l’itinéraire et de créer le CrisisDetailComponent.

Générez un CrisisDetailResolverfichier de service dans la Crisis Centerzone de fonctionnalités.

src / app / centre de crise / crise-détail-résolver.service.ts (généré)

Intégrez les éléments pertinents de la logique de récupération de crise CrisisDetailComponent.ngOnInit et déplacez-les dans la CrisisDetailResolverService. Importez le Crisismodèle CrisisServiceet Router faites- le pour que vous puissiez naviguer ailleurs si vous ne pouvez pas aller chercher la crise.

Soyez explicite. Implémenter l' Resolveinterface avec un type de Crisis.

Injectez CrisisServiceand Routeret implémentez la resolve()méthode. Cette méthode peut renvoyer une Promise, une Observableou une valeur de retour synchrone.

La CrisisService.getCrisisméthode retourne un observable, afin d’empêcher le chargement de la route jusqu’à ce que les données soient extraites. Les Routergardes ont besoin d'un observable pour completesignifier qu'il a émis toutes ses valeurs. Vous utilisez l' takeopérateur avec un argument de 1pour vous assurer que l'observable se termine après avoir récupéré la première valeur de l'observable renvoyée par la getCrisisméthode.

S'il ne retourne pas une valeur valide Crisis, retourne une valeur vide Observableen annulant la navigation en vol précédente à la CrisisDetailComponent et retourne l'utilisateur à la CrisisListComponent. Le service de résolution de mise à jour ressemble à ceci:
src / app / centre de crise / crise-détail-résolver.service.ts

Importez ce résolveur dans crisis-center-routing.module.ts et ajoutez un resolveobjet à la CrisisDetailComponentconfiguration de la route.
src / app / centre de crise / centre de crise / routage.module.ts (résolveur)

Le CrisisDetailComponentne devrait plus aller chercher la crise. Mettez CrisisDetailComponentà jour le pour obtenir la crise de la ActivatedRoute.data.crisispropriété à la place; c'est là que vous avez dit que cela devrait être lorsque vous avez reconfiguré l'itinéraire. Il sera là quand on le CrisisDetailComponentdemandera.
src / app / centre de crise / crise-détail / crise-détail.component.ts (ngOnInit v2)

Deux points critiques

  • L'interface Resolve du routeur est facultative. Le CrisisDetailResolverServicen'hérite pas d'une classe de base. Le routeur recherche cette méthode et l'appelle si elle est trouvée.
  • Fiez-vous au routeur pour appeler le résolveur. Ne vous inquiétez pas de toutes les façons dont l'utilisateur pourrait naviguer. C'est le travail du routeur. Écrivez cette classe et laissez le routeur la prendre à partir de là.

Paramètres de requête et fragments