Ce TP est le dernier d'une série où vous créez la base d'une application permettant d'afficher et de gérer une liste de signets.
Dans les TP précédents, vous avez vu comment accéder à une API à l'aide de l'API fetch
, mettre en place un routage coté client à l'aide de Wouter et gérer des données globales pour l'application à l'aide des contextes React, notamment pour gérer l'utilisateur connecté.
Ce dernier TP, se focalisera sur l'édition de données et la mise en place de formulaires.
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.
Assez vite, le routeur peut être encombré par un grand nombre de routes. Afin d'organiser les routes dans le routeur, vous allez regrouper ensemble les routes concernant les signets. Cependant, avant de vous occuper des routes, vous allez simplifier l'utilisation de la route de la liste des signets en déplaçant la page affichée dans un context d'application.
Vous commencerez donc par créer un nouveau contexte React pour l'application.
Ensuite, vous définirez un nouveau fournisseur (« provider ») pour ce contexte, qui contiendra
page
initialisée à 1
.
value
contenant un object contenant la variable d'état page
et sa fonction de modification.
value
lorsque la variable d'état page
est modifiée.
La valeur associée au fournisseur de contexte sera bien-sûr value
.
Dans le composant App
, vous remplacerez la définition de la variable d'état page
par la mise en place du contexte de l'application. Et dans la vue de la liste des signets, vous pourrez utiliser la variable page
du contexte de l'application pour remplacer la props page
.
Aprés avoir nettoyé le code des composants intermédiaire, vous pourrez contrôler que l'application fonctionne toujours correctement.
App
.
BookmarksList
.
Vous allez maintenant ajouter et regrouper les routes permettant d'interaction avec les signets.
Pour commencer, vous allez créer trois vues pour les signets :
BookmarkCreate
pour la création d'un signet, il affichera pour l'instant un message informatif du type « bookmark creation »,
BookmarkDelete
pour la suppression d'un signet, il affichera pour l'instant un message informatif du type « bookmark {id} supression » où {id} est l'identifiant du signet à supprimer transmis comme paramètre de route,
BookmarkUpdate
pour l'édition d'un signet, il affichera pour l'instant un message informatif du type « bookmark {id} edit » où {id} est l'identifiant du signet à éditer transmis comme paramètre de route.
Comme nous l'avons vu dans le TP précédent, pour extraire le paramètre de route id
, vous pouvez utiliser le hook useParams de Wouter.
Vous allez ensuite ajouter les routes correspondantes à ces vues au routeur de l'application, mais pour organiser vos routes, vous allez créer un nouveau script « bookmarksRoutes.jsx » dans le répertoire « /src/routes ». Ce script exposera une unique fonction bookmarksRoutes
dont le role est de fournir les routes relatives aux signets.
Cette fonction
base
correspondant au chemin de base des routes, « /bookmarks » par exemple,
Route
Wouter :
${base}
/create » affichant la vue BookmarkCreate
,
${base}
/:id » affichant la vue BookmarkDetail
,
${base}
/:id/update » affichant la vue BookmarkUpdate
,
${base}
/:id/delete » affichant la vue BookmarkDelete
,
${base}
» affiche la liste des signets.
Vous utiliserez ensuite cette fonction pour ajouter les routes de gestion des signets au routeur de l'application, vous utiliserez « /bookmarks » comme base pour les routes.
Maintenant que vous disposez d'une route affichant la liste des signets (« /bookmarks »), vous en profiterez pour mettre une redirection sur le chemin de base de l'application (« / »), afin d'éviter la dupliquation de code.
Vous devriez maintenant pouvoir contrôler que les nouvelles routes sont fonctionnelles en y accédant directement en modifiant l'url.
La navigation entre les différentes routes, en modifiant l'URL de la page, est un peu fastidieuse. Vous allez créer un composant permettant de définir un menu de navigation. Le menu devra être construit spécifiquement pour chaque vue, l'objectif sera donc de simplifier la création de ce menu de navigation.
Vous commencerez par créer un composant MenuButton
recevant comme propriétés React (props) :
icon
une icône FontAwesome obligatoire à afficher dans le bouton,
to
la route vers laquelle rediriger l'application.
La définition de la PropType de icon
sera un objet JavaScript contenant les propriétés prefix
et iconName
de type string
et isRequired
.
Le composant retournera un lien Wouter redirigeant vers la route to
. Le support du lien sera un bouton HTML de type button
contenant l'icône FontAwesome icon
.
Vous allez maintenant créer le menu de navigation, sous la forme d'une barre de navigation avec deux zones de boutons optionnelles, à gauche et à droite, comme illustré dans la maquette suivante :
Vous définirez donc un composant Menu
recevant comme propriétés React (props) left
et right
deux tableaux (vides par défaut). Les tableau pourront contenir des objets avec des propriétés permettant de générer des MenuButton
: icon
et to
.
Un exemple de construction de menu pourrait être le suivant :
Vous générerez des tableaux de MenuButton
pour les propriétés React (props) left
et right
que vous retournerez, encapsulés dans un élément HTML de type nav
. Vous pourrez ajouter le balisage nécessaire pour obtenir le rendu souhaité.
MenuButton
.
Menu
.
Vous allez maintenant mettre en place la navigation entre les différentes routes de l'application dans chaque vue.
BookmarksList
vous ajouterez un bouton à droite vers la création d'un signet.
BookmarkCreate
vous ajouterez un bouton à gauche permettant de revenir vers la liste des signets.
BookmarkDetail
vous ajouterez un bouton à gauche permettant de revenir vers la liste des signets et deux boutons à droite vers la modification et la suppression du signet.
BookmarkDelete
vous ajouterez un bouton à gauche permettant de revenir vers le détail du signet.
BookmarksList
vous ajouterez un bouton à gauche permettant de revenir vers le détail du signet.
Vous devriez maintenant pouvoir contrôler que les routes sont toujours fonctionnelles en passant par l'interface.
Vous allez maintenant permettre à l'utilisateur de créer un nouveau signet.
Pour commencer, vous créerez un nouveau composant BookmarkForm
qui affichera un formulaire HTML permettant de saisir les données d'un signet.
Le composant recevra comme propriété React (props
) :
title
le titre du formulaire,
submit
le texte du bouton de soumission,
onSubmit
la fonction à invoquer lors de la soumission du formulaire.
Les informations minimales nécessaires à l'API pour créer un signet sont un nom, une URL, une description et un attribut rendant le signet public ou privé.
React propose deux approches pour gérer les formulaires :
La documentation de la seconde approche vous montre une solution reposant sur la propriété target
de l'événement onSubmit
. C'est cette solution que vous allez employer en ajoutant un gestionnaire d'événements sur la soumission du formulaire, vous permettant d'accéder aux éléments du formulaire grâce à l'événement transmis en paramètre.
Vous pourrez alors invoquer la fonction onSubmit
en lui transmettant en paramètre les données du signet à créer. Vous veillerez à prévenir le comportement par défaut de l'événement pour empêcher le rechargement de la page.
Pour finir, vous utiliserez ce composant dans la vue de la création d'un signet en transmettant la fonction console.log
comme props onSubmit
. Vous devriez maintenant pouvoir tester votre formulaire et obtenir l'affichage des données dans la console de développement.
Il est possible qu'ESLint trouve à redire sur votre gestion des labels dans votre formulaire, comme d'habitude, vous pouvez tout à fait venire corriger les règles de la configuration ESLint pour les adapter à votre code :
BookmarkForm
.
BookmarkForm
dans la vue BookmarkCreate
.
Pour finir, vous allez réaliser la création du signet dans l'API.
Vous commencerez par ajouter un nouveau service d'interaction avec l'API sous la forme d'une fonction postBookmark(data)
au script « src/services/api/bookmarks.js », permettant la création d'un signet.
Cette interaction doit se faire en étant authentifié, vous penserez donc bien à transmettre votre cookie de session lors de vos requêtes. Vous pourrez utiliser la méthode de classe JSON.stringify()
pour générer le contenu de la requête. Et le type mime de la requête pour le contenu transmis à l'API doit être : « application/ld+json ».
Pour finir, dans la vue BookmarkCreate
, vous ferez en sorte que la soumission du formulaire :
useLocation
de Wouter
.
Lorsque le signet créé est privé, il n'est pas accessible aux autres utilisateurs et vous ne pouvez voir vos signets privés que si vous êtes authentifié. Vous veillerez donc à contrôler que vos requêtes fetchAllBookmarks
et fetchBookmark
fournissent bien votre cookie de session afin d'obtenir les signets privés aussi.
postBookmark(data)
au script src/services/api/bookmarks.js.
postBookmark(data)
dans la vue BookmarkCreate
.
Sur le même principe, pour éditer un signet, vous ajouterez une fonction patchBookmark(data)
au script src/services/api/bookmarks.js, permettant l'édition d'un signet.
Le composant BookmarkForm
que vous avez créé précédemment pour la création d'un signet pourra être réutilisé pour l'édition d'un signet. Vous ajouterez donc une nouvelle propriété React (props) optionnel bookmarkData
pour lui transmettre les données du signet à éditer. Vous utiliserez ces données pour initialiser les champs du formulaire lorsqu'elles sont présentes.
Dans la vue BookmarkUpdate
, de manière similaire à la vue BookmarkDetail
, vous réaliserez une requête permettant d'obtenir les données du signet à éditer. Vous utiliserez ces données pour initialiser le formulaire de modification du signet.
Enfin, pour finir, lors de la soumission du formulaire, vous ferez en sorte que le composant :
Vous pourrez contrôller que tout fonctionne correctement, notamment que la vue de la création d'un composant est toujours fonctionnel.
patchBookmark(id, data)
au script src/services/api/bookmarks.js.
BookmarkForm
.
BookmarkForm
dans la vue BookmarkUpdate
.
patchBookmark(id, data)
dans la vue BookmarkCreate
.
Pour finir les interactions avec l'API, vous allez ajouter la suppression d'un signet. Vous commencerez par ajouter une fonction deleteBookmark(id)
au script src/services/api/bookmarks.js, permettant la suppression d'un signet dans l'API.
Vous ajouterez ensuite à la vue BookmarkDelete
un message demandant la confirmation pour la suppression du signet, ainsi que deux boutons ; pour annuler ou confirmer la suppression.
Le bouton d'annulation basculera l'affichage vers la vue du détail du signet.
Le bouton de confirmation :
deleteBookmark(id)
au script src/services/api/bookmarks.js.
BookmarkDelete
.
deleteBookmark(id)
dans la vue BookmarkDelete
.
L'application est fonctionnelle et la sécurité des données est assurée par l'API. Cependant, l'expérience utilisateur n'est pas optimale sans gestion des rôles des utilisateurs. Par exemple, un utilisateur anonyme ne devrait pas avoir accès à la zone de création d'un signet. Ni un utilisateur accès à la modification d'un signet ne lui appartenant pas.
Pour commencer, vous allez cacher les boutons n'ayant pas sens dans l'interface. Vous ajouterez une nouvelle propriété React (props) hidden
au composant MenuButton
. Il s'agira d'un booléen et s'il vaut true
le composant retourne null
sans rien afficher.
Afin d'obtenir la valeur à transmettre, vous allez créer un hook personalisé permettant de savoir si l'utilisateur courant est connecté ou si l'IRI d'une ressource de l'API correspond à l'utilisateur connecté. Vous définirez donc le hook usePermissions
qui retournera un object JavaScript comportant 2 propriétés :
isAuthenticated
, contenant un booléen définissant si un utilisateur est définie dans le context de l'utilisateur courant,
isOwner
, contenant une fonction recevant l'IRI d'un propriétaire de ressource de l'API et qui retournera un booléen définissant si cet IRI correspond à l'utilisateur courant.
Vous pourrez utiliser ce hook pour définir les valeurs des boutons de navigation qui doivent être supprimés de l'interface en fonction de l'utilisateur :
Maintenant votre interface est plus cohérent, mais même si les routes ne sont pas accessibles par l'interface, elles sont tout de même disponibles par l'URL, ce qui peut amener à des comportements contre-intuitifs de l'application, notamment lors de la déconnexion de l'utilisateur. Vous allez donc utiliser le composant Redirect
de Wouter pour sécurisé les vues privées :
BookmarkCreate
, l'utilisateur doit être authentifié,
BookmarkUpdate
et BookmarkDelete
, l'utilisateur doit être le propriétaire du signet.
hidden
au composant MenuButton
.
hidden
dans les différentes vues de gestion des signets.