The playground

More information here

UX plus fluide au chargement de la page avec les résolveurs Angulaires

En tant que développeur, vous cherchez toujours à optimiser votre code. Cela inclut la vitesse à laquelle vous présentez à l’utilisateur l’interface utilisateur entièrement chargée, qui dépend souvent des données provenant d’une base de données. Inévitablement, vous commencez à chercher des moyens de résoudre les données immédiatement après la navigation vers une nouvelle page / […]

En tant que développeur, vous cherchez toujours à optimiser votre code. Cela inclut la vitesse à laquelle vous présentez à l’utilisateur l’interface utilisateur entièrement chargée, qui dépend souvent des données provenant d’une base de données.

Inévitablement, vous commencez à chercher des moyens de résoudre les données immédiatement après la navigation vers une nouvelle page / zone de votre application, sans que l’utilisateur ne rencontre ce que l’on appelle page JANK.

C’est lorsque la page se déplace de haut en bas pendant le chargement de certains composants. Cela peut affecter considérablement l’UX au point où il semble « bogué ».

Angular fournit une approche intuitive de la pré-récupération des données avant le chargement de la route ; avant la résolution de la route parcourue.

C’est ce qu’on appelle un résolveur angulaire.

Un résolveur angulaire est essentiellement un service angulaire. Une classe injectable que vous fournissez à un module de routage dans la configuration de routage. Ce type spécial de service est injecté et exécuté lorsque la route contenant est parcourue.

Le résolveur résout ensuite les données avant le chargement de la page, qui devient disponible via le service ActivatedRoute. Cela fournit un moyen simple et efficace de s’assurer que votre utilisateur dispose des données le plus rapidement possible avant qu’un composant important pour le chargement initial de la page en ait besoin.

Une autre façon d’utiliser un résolveur angulaire consiste à l’utiliser comme méthode pour remplir instantanément les métadonnées SEO.

Avec un résolveur, vous garantissez que les données existeront avant le chargement de la page, en vous assurant que tout a ce dont il a besoin lors de l’initialisation.

Décomposons le résolveur angulaire

Un résolveur angulaire est une classe qui implémente l’interface Resolve. L’interface Resolve nécessite que vous implémentiez une fonction dans la classe appelée resolve.

Voici la signature de l’interface de résolution…

export interface Resolve<T> {
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<T> | Promise<T> | T {
return 'data';
}
}

Comme on peut le voir sur la signature de l’interface, elle nécessite un argument générique T qui sera le type de nos données résolues.

La fonction resolve renvoie un ObservablePromise ou simplement les données de type T. Par conséquent, il est suggéré que cela soit géré de manière asynchrone, en particulier si vous récupérez les données de la base de données.

Le but principal de la fonction de résolution est qu’elle doit être terminée. Ce fait doit être rappelé lors de la récupération des données dans les observables. L’observable doit se terminer. Après tout, c’est un résolveur.

Si l’observable ne se termine pas, les données ne seront jamais résolues et la page ne se chargera jamais. Par conséquent, vous devez définir le point auquel vous n’avez plus besoin de prendre de valeurs et que les données peuvent résoudre, car vous avez tout ce dont vous avez besoin de la base de données. Lors de l’utilisation de flux de données asynchrones tels que les observables, il s’agit d’un cas d’utilisation pour les opérateurs pipeables de RXJS.

Les opérateurs pipeables qui me viennent à l’esprit lorsque je pense à compléter un flux de données en fonction d’une condition, sont une combinaison de filtertakefirst. Avec cette combinaison, vous pouvez filtrer toutes les valeurs que vous ne souhaitez pas prendre, telles que null ou undefined ou un tableau vide , puis take la première valeur valide avec take(1).

Les opérateurs qui peuvent être nécessaires pour terminer une observable tôt en cas de problèmes ou d’erreurs, dans lesquels vous voudrez renvoyer null ou rediriger, sont catchError et timeout. Une combinaison de timeout et catchError est utile si vos données prennent trop de temps et que vous souhaitez renvoyer null afin que vous puissiez réessayer dans le composant, ou que vous souhaitiez rediriger.

Si vos données ne se résolvent pas rapidement, sont basées sur un filtrage complexe, une logique, d’énormes quantités d’appels de base de données, vous risquez de rencontrer des problèmes de temps en temps.

Il est préférable de déterminer le moins d’appels de base de données et le minimum de données nécessaires pour charger correctement et correctement la page.

Par conséquent, vous pouvez bénéficier de passer un certain temps, avant d’implémenter votre résolveur, à vous concentrer sur la séparation du contenu « au-dessus du pli » des données qui peuvent être chargées lors de l’initialisation de la page.

Par conséquent, vous pouvez diviser les données nécessaires à une expérience utilisateur fluide, à partir du reste des données qui pourraient être appelées à partir du composant, plutôt que du résolveur.

Vous pouvez alors traiter exclusivement le contenu minimal, au-dessus du pli, via le résolveur.

Cette approche dynamique du chargement de page peut être assistée par l’utilisation de squelettes. De sorte que si l’utilisateur fait défiler instantanément vers le bas, vous pouvez lui indiquer que le contenu est en cours de chargement, améliorant ainsi l’UX.

Étape 1: Création du résolveur

Nous devons créer le résolveur angulaire. Cependant, il n’y a pas de commande CLI angulaire qui génère un résolveur. Par conséquent, nous devrons écrire nous-mêmes le décorateur (métadonnées du résolveur).

Heureusement, ce ne sont que quelques lignes de code qui forment le passe-partout pour un résolveur, et nous pouvons prendre le décorateur injectable d’un service existant si vous avez du mal à vous en souvenir.

Annoter la classe de résolveur de profil avec un décorateur injectable

Tout d’abord, nous fournissons au décorateur injectable providedIn: any dans la configuration.

@Injectable({ providedIn: 'any'})

Nous nommerons ensuite notre résolveur en ajoutant la convention Resolver. Pour cet exemple, nous allons résoudre les données de profil (données utilisateur), nous l’appellerons donc ProfileResolver.

Comme il s’agit d’un résolveur et qu’Angular reconnaît la fonction des résolveurs, nous pouvons implémenter la classe Resolve, qui fournira la signature que nous devons implémenter dans notre fonction de résolution pour résoudre avec succès les données.

@Injectable({providedIn: 'any'})
export class ProfileResolver implements Resolve<Profile> {
}

Notre fonction de résolution renverra une observable avec des données conformes à l’interface Profile. Par conséquent, nous fournirons l’interface Profile comme argument générique de la classe de résolveur et de la fonction resolve(). De cette façon, nous nous sommes conformés aux exigences angulaires d’un résolveur.

resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<T> | Promise<T> | T {
return;
}

L’implémentation de resolve nous donne deux paramètres s’ils sont nécessaires, route et state. Ceux-ci sont automatiquement remplis et sont accessibles depuis notre fonction resolve().

Ensuite, nous devons réellement résoudre les données réelles de la base de données, ce qui sera notre prochaine étape.

Récupération des données de la base de données

Pour récupérer les données pour notre fonction de résolution, nous devrons injecter le service qui fournit les données; celui qui interagit avec la base de données.

Nous prenons ce dont nous avons besoin pour résoudre rapidement afin que l’utilisateur navigue rapidement et avec succès. Pour les besoins de cette démo, nous ne nous inquiéterons pas du service sous-jacent qui traite de la base de données. Nous allons simplement injecter le service en utilisant l’injection de dépendance dans l’argument constructeur pour notre classe ProfileResolver.

Comme nos données se présentent sous la forme d’un flux de données observable avec plusieurs valeurs émettant de manière asynchrone, il nous suffira de take(1) en utilisant l’opérateur pipeable take importé de rxjs/operator. Sinon, l’observable ne se terminerait jamais, et le résolveur ne se résoudrait jamais.

Nous avons juste besoin d’une émission / valeur et take complète l’observable pour nous.

C’est aussi simple que cela de créer un résolveur ; il suffit de renvoyer l’observable dans la fonction resolve() dont angular gérera l’abonnement.

La classe ProfileResolver

Nous gérons toutes les erreurs lors de la récupération des données en redirigeant vers une route parent.

Bonus: Remplissez rapidement les métadonnées SEO dynamiques avant que l’itinéraire ne soit chargé

Les avantages de remplir nos balises <meta> présentent instantanément des avantages évidents. Plus nos métadonnées SEO sont remplies rapidement, plus notre référencement reflète correctement et avec précision le contenu de la page.

Cela signifie qu’il est plus facile et plus rapide pour les robots, tels que ceux exploités par les moteurs de recherche tels que Google et Bing, d’explorer votre site et d’en récupérer le contenu.

Ce n’est pas si important sur les pages pré-rendues ou celles qui sont rendues par Angular Universal, car tout le rendu est terminé avant que les robots ne reçoivent le contenu.

Cependant, si vous comptez sur la capacité (souvent discutable) des robots Google à analyser javascript pour votre référencement, ou si vous avez une solution à la demande comme puppeteer qui a besoin de l’assurance que le référencement sera correct avant de renvoyer le DOM rendu, alors un résolveur qui inclut le référencement devrait vous aider. Cela aide donc lorsque le robot est limité dans le temps.

Il sépare également les préoccupations du composant, de sorte que le composant n’a pas à gérer quoi que ce soit lié au référencement. Une des principales raisons pour lesquelles j’aime les résolveurs.

Étape 2 : Injectez le résolveur dans le module de routage

Le module ProfileRoutingModule où nous fournirons notre résolveur est un module chargé paresseux. Par conséquent, notre chemin racine sera vide avec le jeton de paramètre userSlug, dont nous aurons besoin pour récupérer les données de profil correctes.

Pour fournir notre résolveur, nous fournissons simplement un objet avec le nom de nos données comme clé et le résolveur spécifique comme valeur qui sera responsable de la résolution de ces données.

Vous pouvez nommer la clé comme vous le souhaitez, mais nous l’appellerons simplement data.

Notre module de profilerouting

C’est tout ce qui est nécessaire dans le module de routage pour que nous puissions utiliser notre résolveur.

Ensuite, nous devons récupérer et utiliser nos données dans le composant.

Étape 3: Initialisez le composant avec les données résolues

Maintenant que nous avons résolu nos données lors de l’activation de la route, les données sont accessibles via le service ActivatedRoute. Comme nous avons affaire à des observables, tout au long de l’application, nous allons créer un flux qui se lie à la propriété data qui sera nos données résolues.

Tout d’abord, nous injecterons le ActivatedRoutedans le constructeur de notre ProfileComponent. Ensuite, nous affecterons this.route.data à l’observable profile$. Nous voudrons également passer à l’utilisation d’un observable lorsque des données mises à jour arrivent de la base de données afin que nous ayons de nouvelles données lorsque nous interagissons avec l’application.

Pour cela, nous utiliserons startWith afin de démarrer notre flux avec la valeur facilement accessible depuis this.route.snapshot.data. Nous accédons ensuite à la propriété data comme this.route.snapshot.datastartWith indique une valeur pour commencer, comme première émission de notre flux.

Notre composant de profil

Ce que les données immédiatement accessibles font pour le composant

Les données immédiatement accessibles réduisent le temps passé à charger les différentes parties de cette page, ce qui est observé par l’utilisateur. Le résultat de ne pas utiliser un résolveur comme celui-ci est que la page peut sembler se charger de manière fragmentée, ce qui n’est pas visuellement agréable.

Par conséquent, vous devrez faire attention aux éléments de vos modèles HTML qui dépendent de quelles données. Vous devez ensuite écrire votre résolveur pour prendre en charge ces éléments et l’effet global sur l’UX de chargement de page.

Il existe plusieurs façons dont le composant peut charger fragmenté

  • L’une d’entre elles est si vous avez un ngIf dans plusieurs parties de votre modèle HTML.
  • Un autre est ngFor.

Il est recommandé de limiter la quantité d’individus ngIf que vous écrivez dans le but de limiter la quantité de redimensionnement du navigateur.

Le chargement de la page avant d’obtenir les données peut provoquer un saut, un décalage et un redimensionnement constants de certaines parties de votre page, ce qui fait souffrir l’UX.

L’implémentation d’un résolveur peut faire la différence entre 3 à 5 secondes de saut et de redimensionnement par rapport à 0,5 seconde, ce qui est souvent trop rapide pour nuire à l’UX globale.

C’est tout! Nous avons un résolveur avec une expérience utilisateur améliorée lors du chargement de la page.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.