Ce TP est le premier d'une série où vous allez créer la base d'une application permettant d'afficher et de gérer une liste de signets.
Afin que les données puissent être partagées entre différents utilisateurs, elles seront centralisées dans une base de données et accédées à l'aide d'une API.
Pour simplifier la conception et la gestion de l'application, les données de l'application seront gérées à l'aide des contextes React et l'affichage des composants sera réalisé à l'aide d'un routeur.
Ce TP, se focalisera sur la mise en place du projet et la réalisation d'une première requête AJAX.
Les notes des TP seront obtenues à partir de vos dépôts Git, vous devrez donc prendre grand soin de la qualité de ces dépôts et de leurs « commits ».
De manière similaire, les descriptions de vos « commits » devront être claires et informatives, afin de permettre l'évaluation de la progression de votre travail.
Vous allez créer un nouveau projet React qui se nommera r410-bookmarks
. Vous commencerez par créer un sous-répertoire r410-bookmarks dans le répertoire React pour accueillir votre nouveau projet.
Pour faciliter la mise en place du projet, vous recopierez dans ce sous-répertoire les fichiers de base du projet précédent :
Vous mettrez à jour le projet dans les fichiers package.json et index.html. Vous supprimerez le contenu du fichier index.css. Et vous supprimerez le contenu du composant App
.
Ensuite, vous ajouterez la modification nécessaire à la structure de l'application et au CSS pour que la structure de base de votre application soit similaire à la maquette suivante :
Vous fixerez la largeur de l'application, qui restera centrée dans la fenêtre du navigateur si sa largeur est supérieure. Et la hauteur de l'application devra utiliser tout l'espace disponible. La partie centrale devra occuper tout l'espace non requis par l'en-tête et le pied de page.
Puis, vous créerez un dépôt distant associé, nommé lui aussi « r410-bookmarks » auquel vous associerez votre dépôt local. Vous penserez à ajouter votre intervenant de TP comme membre du dépôt distant, avec un rôle au moins de Reporter.
Vous allez commencer par afficher la liste des signets. Dans cette optique, vous allez créer un composant BookmarkList
, qui réalisera une requête AJAX vers l'API et affichera, pour l'instant, la liste des noms de signets obtenus.
Dans un premier temps et afin d'organiser votre code, vous centraliserez tout le code relatif à l'API de donnée dans le fichier src/services/api/bookmarks.js. Vous y définirez la constante BASE_URL
:
Vous y ajouterez une fonction fetchAllBookmarks()
qui utilisera la constante BASE_URL
pour réaliser une requête AJAX à l'aide la fonction fetch()
vers la ressource « /bookmarks » de l'API. Elle retournera la promesse du contenu du JSON de la réponse de la requête AJAX.
Comme vous l'avez vu au semestre précédent, la fonction fetch()
permet de réaliser une requête AJAX et de gérer le résultat asynchrone à l'aide de Promesses. Dans notre cas, la réponse sera de type JSON et le contenu sera obtenu à l'aide de la méthode json()
de la réponse.
ESLint devrait vous indiquer une erreur sur l'utilisation de fetch. Le linter vous indique que cette fonction est encore experimentale dans le cadre de Node, mais elle est bien supportée dans les navigateurs. Vous installerez le module JavaScript eslint-plugin-n (npm install eslint-plugin-n --save-dev) et vous désactiverez cette erreur pour ce projet dans la configuration d'ESLint :
Vous pourrez ensuite créer le composant BookmarkList
. Ce composant comportera une variable d'état bookmarksData
contenant la liste des signets à afficher.
En utilisant un hook d'effet, vous réaliserez une requête vers l'API afin d'obtenir une liste des signets à afficher, que vous stockerez dans la variable d'état bookmarksData
, lors de la réponse de l'API.
Dans un premier temps, vous vous contenterez d'afficher le nom de chaque signet, de manière similaire à la maquette suivante :
Le composant BookmarkList
se composera d'un élément HTML de type section comportant une liste d'articles affichant le nom de chaque signet. Vous utiliserez ensuite le composant BookmarkList
dans le composant App
.
fetchAllBookmarks()
dans le fichier « src/services/api/bookmarks.js ».
BookmarkList
.
BookmarkList
dans le composant App
.
Vous allez avoir besoin d'afficher des notes, vous allez donc créer un composant Rating
permettant d'afficher une note.
Le composant Rating
recevra une props value
contenant la note à afficher. Vous illustrerez la note à l'aide d'une icône étoile de Font Awesome. Et vous pourrez afficher la note en vous inspirant des maquettes suivantes :
La props value
peut contenir une valeur réelle avec de nombreuses décimales, vous prendrez soin de l'arrondir à la décimale la plus proche avant de l'afficher, à l'aide Math.round
par exemple.
Rating
.
Vous créerez ensuite un composant BookmarkItem
dont le rôle sera d'afficher un signet dans la liste des signets.
Ce composant recevra une propriété data
contenant les données d'un des signets de la liste issue de l'API et affichera sa représentation de manière similaire à la maquette suivante :
Le composant affichera le nom du signet, qui sera le support d'un lien vers l'URL du signet. En plus du nom, la moyenne des notes et l'avatar de l'utilisateur ayant créé le signet seront affichés.
Pour afficher l'avatar d'un utilisateur, vous utiliserez un élément HTML img
et pour obtenir l'URL de l'image, vous ajouterez une fonction avatarUrl(userId)
au script « bookmarks.js ». Cette fonction recevra en paramètre l'identifiant d'un utilisateur et retournera l'URL de la ressource de l'API fournissant l'avatar de l'utilisateur correspondant.
Vous prendrez soin de définir les prop-types du composant.
BookmarkItem
.
avatarUrl(userId)
.
BookmarkItem
dans le composant BookmarkList
.
Si vous observez la réponse de la liste des signets, vous constaterez que le résultat est paginé et que votre application affiche systématiquement la première page de la requête. Vous allez ajouter le support de la pagination à votre application.
Les données de pagination exportées par l'API ne sont pas très faciles à manipuler. Avant de créer le composant de pagination, vous allez transformer les données de l'API en une structure plus exploitable par votre futur composant.
Dans cette optique, vous ajouterez un module JavaScript « paginationFromCollectionView.js » dans le répertoire « src/services/transformers » de votre projet. Ce script exportera par défaut une fonction qui recevra en paramètre le contenu de la propriété view
d'une réponse paginée de l'API et retournera un objet JavaScript comportant les propriétés suivantes :
current
, une propriété obligatoire contenant la page courante (un nombre), extraite de la propriété @id
de la vue.
first
, une propriété contenant la première page (un nombre), extraite de la propriété first
de la vue.
previous
, une propriété optionnelle contenant la page précédente (un nombre), extraite de la propriété previous
de la vue si elle est présente.
next
, une propriété optionnelle contenant la page suivante (un nombre), extraite de la propriété next
de la vue si elle est présente.
last
, une propriété contenant la dernière page (un nombre), extraite de la propriété last
de la vue.
Vous pourrez utiliser le squelette de fichier suivant :
Le paramètre contenant la vue de la réponse paginée de l'API peut être non-défini, si la réponse ne contient qu'une page. Dans ce cas, la fonction devra retourner null
.
Pour simplifier l'extraction de la valeur de la page des différentes urls, vous pourrez utiliser la classe URLSearchParams
.
Afin de tester la validité de votre fonction, vous afficherez le résultat de son utilisation sur la pagination de la liste de signets obtenue dans le composant BookmarkList
.
paginationFromCollectionView
dans « paginationFromCollectionView.js ».
BookmarkList
.
Vous ajouterez une variable d'état au composant BookmarkList
stockant la pagination générée à partir de la propriété view
de la réponse paginée de l'API.
Vous allez ensuite créer un composant Pagination
recevant comme propriété la variable d'état précédente et affichant la page courante ainsi que des boutons vers les première et dernière pages, ainsi que les pages précédente et suivante. Les boutons devront être désactivés lorsque les propriétés ne sont pas présentes dans la pagination.
Vous ajouterez une instance du composant Pagination
au composant BookmarkList
afin d'afficher l'interface de navigation de la pagination pour la liste des signets.
Pagination
.
Vous disposez maintenant d'une interface de navigation pour la liste des signets, mais celle-ci n'est pas encore fonctionnelle. Afin de pouvoir faire évoluer la page courante de la liste des signets, vous allez ajouter une variable d'état page
au composant BookmarkList
avec 1 comme valeur initiale.
Vous réaliserez la mise à jour de cette variable d'état lors du clic sur un des boutons du composant Pagination
à l'aide d'une nouvelle props contenant une fonction de rappel, réalisant la mise à jour de la variable d'état page
.
Vous pourrez ensuite ajouter un paramètre urlParams
de type URLSearchParams
à la fonction fetchAllBookmarks
dans lequel seront spécifiés les paramètres de la requête AJAX, pour l'instant, il ne contiendra que la page souhaitée.
Pour finir, afin que la requête AJAX soit ré-exécutée lors de chaque modification de la variable d'état page
, vous placerez cette variable d'état dans la liste des dépendances du hook d'effet encapsulant la requête.
page
stockant la page courante.
Pagination
permettant de mettre à jour la variable d'état page
.
Afin d'améliorer l'expérience utilisateur, il est courant d'utiliser un indicateur de chargement lors du chargement de ressources.
Vous commencerez par créer un nouveau composant Loading
.
Dans le composant BookmarkList
, vous initialiserez la variable d'état contenant la liste des signets à null
avant chaque requête AJAX.
Lors de l'affichage, vous ferez en sorte d'afficher le composant Loading
en lieu et place du tableau de composants BookmarkItem
lorsque la liste des signets vaut null
.
Loading
.
Pagination
dans BookmarkList
.