La modification et la suppression des données sont, bien sûr, des fonctionnalités essentielles dans la majorité de nos applications.
Ce qui est facilité par le state management réactif, c'est de peaufiner l'approche des deux fonctionnalités.
Pour la suppression, en général, il vaut mieux être pessimiste – attendre que le serveur confirme la suppression d'un élément avant de le supprimer du cache de l'application.
Pour la modification, on peut être plus optimiste – modifier la donnée directement dans le cache, sans attendre la réponse du serveur :
l'avantage principal est de lisser l'expérience utilisateur – il n'attend pas qu'une requête s'envoie pour continuer à travailler ;
l'inconvénient est la possibilité qu'il y ait un écart entre les données affichées à l'utilisateur et celles enregistrées en base.
Le choix du régime entre optimiste et pessimiste dépendra de l'importance de l'équilibre entre ces deux facettes : expérience vs écart de données.
Dans les deux cas, vous allez considérer qu'il n'y a pas besoin de renvoyer de requête GET pour récupérer les données.
Vous effectuerez la même opération (modification ou suppression) sur les données en cache, puis vous redirigerez l'utilisateur vers la list view. Le service déterminera s'il faut redemander les données : si ça fait plus de 5 minutes.
Commencez par implémenter le refus d'un candidat : la suppression.
Soyez pessimiste en supprimant
Le service
Comme pour la méthode getCandidatesFromServer()
, la méthode refuseCandidate()
ne retournera pas un Observable. Appeler la méthode va simplement déclencher la suppression du candidat, et le montage de state management réactif s'occupera du reste !
Voici la méthode :
refuseCandidate(id: number): void {
this.setLoadingStatus(true);
this.http.delete(`${environment.apiUrl}/candidates/${id}`).pipe(
delay(1000),
switchMap(() => this.candidates$),
take(1),
map(candidates => candidates.filter(candidate => candidate.id !== id)),
tap(candidates => {
this._candidates$.next(candidates);
this.setLoadingStatus(false);
})
).subscribe();
}
Il s'agit d'une suppression pessimiste. Vous attendez donc que la requête réussisse avant de mettre à jour les données côté application :
quand la requête réussit, vous transférez l'Observable vers les
candidates$
à l'instant t ;si vous ne mettez pas le
take(1)
, vous finirez dans un infinite loop ! Tout ce qui vient après ceswitchMap
ne doit être exécuté qu'une seule fois par suppression ;vous utilisez
map
pour modifier le tableau, retournant un tableau qui contient tous les candidats sauf celui qui comporte l'id
passé en argument ;vous faites émettre la nouvelle liste de candidats et l'état de chargement.
C'est cette dernière étape qui fait toute la magie du state management réactif : le reste de l'application n'a pas besoin de suivre l'avancée de la requête de suppression. Les components qui sont souscrits aux Observables du service vont simplement afficher les nouvelles données qui sont émises !
Le component
Côté component, vous n'allez pas attendre que tout le processus de suppression soit terminé avant de rediriger l'utilisateur :
onRefuse() {
this.candidate$.pipe(
take(1),
tap(candidate => {
this.candidatesService.refuseCandidate(candidate.id);
this.onGoBack();
})
).subscribe();
}
Vous avez besoin de l'
id
du candidat pour le supprimer, donc vous partez de l'Observablecandidate$
.Vous utilisez
take(1)
car la logique ici ne doit être exécutée qu'une seule fois par appel.Vous déclenchez la suppression et vous redirigez immédiatement l'utilisateur.
Du coup, l'utilisateur se retrouve sur la list-view avec un spinner pendant une seconde, puis la liste des candidats se met à jour correctement !
Soyez optimiste en modifiant
Le service
Dans cette application, l'embauche d'un candidat change son attributcompany
en 'Snapface Ltd'
.
Pour la modification optimiste, vous allez d'abord mettre à jour les données de l'application avant même d'envoyer la requête au serveur :
hireCandidate(id: number): void {
this.candidates$.pipe(
take(1),
map(candidates => candidates
.map(candidate => candidate.id === id ?
{ ...candidate, company: 'Snapface Ltd' } :
candidate
)
),
tap(updatedCandidates => this._candidates$.next(updatedCandidates)),
switchMap(updatedCandidates =>
this.http.patch(`${environment.apiUrl}/candidates/${id}`,
updatedCandidates.find(candidate => candidate.id === id))
)
).subscribe();
}
Vous transformez puis faites émettre le tableau des candidats à jour, puis vous envoyez une requête PATCH avec le candidat mis à jour ! Aussi simple que ça !
Le component
Côté component, ça se passe exactement de la même manière que pouronRefuse()
:
onHire() {
this.candidate$.pipe(
take(1),
tap(candidate => {
this.candidatesService.hireCandidate(candidate.id);
this.onGoBack();
})
).subscribe();
}
Maintenant, lorsque vous cliquez sur EMBAUCHER, la modification dans l'application est instantanée, et le serait même si le serveur tardait à répondre : c'est l'avantage de la modification optimiste.
Vous avez maintenant un module basé à 100 % sur un state management réactif !
En résumé
Le state management réactif nous permet de modifier notre approche à la modification des données pour être pessimiste ou optimiste.
Une action est pessimiste si vous attendez la réaction du serveur avant de modifier les données dans l'application.
Une action est optimiste si vous modifiez les données de l'application avant même d'envoyer la requête au serveur, permettant une expérience utilisateur beaucoup plus lisse.
Bravo à vous ! Pour bien terminer ce cours, validez vos compétences avec le quiz suivant. Puis, nous ferons un récap de ce que vous avez accompli dans ce cours, et verrons comment continuer votre apprentissage d'Angular.