Réagissez aux réponses

Les requêtes HTTP étant représentées par des Observables, vous pouvez réagir aux réponses pour implémenter des comportements avancés.

Dans l'application Dwelio, vous permettez aux utilisateurs de soumettre une offre pour le bien. Vous allez maintenant implémenter une requête composée qui vérifiera, au moment de soumettre l'offre, si le bien a déjà atteint son nombre maximum d'offres. En fonction de la réponse de cette première requête, soit vous enverrez l'offre au serveur, soit vous afficherez un message à l'utilisateur sans envoyer l'offre.

Limitez les offres

Vous aurez tout d'abord besoin de deux nouvelles méthodes dansHousingService:

checkIfOfferLimitReached(id: string): Observable<boolean> {
    return this.http.get<{
        id: string,
        offerLimitReached: boolean
    }>(`http://localhost:3030/api/properties/${id}/check-offer-limit`).pipe(
        map(response => response.offerLimitReached)
    );
}

makeOffer(id: string, amount: number) {
    return this.http
        .post(`http://localhost:3030/api/properties/${id}/make-offer`, { amount });
}

Regardons ces deux méthodes :

  • la routecheck-offer-limitretourne un objet contenant l'iddu bien et un booléen qui décrit si la limite d'offres a été atteinte. Puisque c'est ce booléen qui nous intéresse, on ajoute unmappour que l'Observable n'émette que lui.

  • La routemake-offeraccepte une requête POST — on doit donc lui passer un corps de requête. C'est bien ce qui se passe ici : la méthodepostde HttpClient accepte le corps de la requête comme deuxième argument, après l'URL.

Du coup, comment faire pour composer ces deux requêtes et utiliser la réponse de la première pour définir la seconde ?

Partons deMakeOfferComponent, là où tout ce processus démarre. Son enfant,MakeOfferFormComponent, expose un événement,formSubmitted, qui émet un objet de typeOfferFormValue. Commencez par créer une méthodeonSubmitFormpour capturer cette émission, puis liez-la au output deMakeOfferFormComponent:

<app-make-offer-form (formSubmitted)="onSubmitForm($event)" />
//...
onSubmitForm(offerFormValue: OfferFormValue): void {
}

Implémentons pas à pas cette méthode. Niveau comportement, la première chose qu'on doit savoir, c'est si la limite d'offres a déjà été atteinte sur le bien en question, donc commençons par :

onSubmitForm(offerFormValue: OfferFormValue): void {
    this.housingService.checkIfOfferLimitReached(this.id).subscribe();
}

Pour l'instant nous ne faisons rien avec la réponse. On sait que cet Observable émettruesi la limite est atteinte, etfalsesinon.

Traitons d'abord le cas où la limite est atteinte. Vous aurez besoin d'injecterModalServicepour afficher le modal qui prévient l'utilisateur que la limite est atteinte :

private modalService = inject(ModalService);
//...
onSubmitForm(offerFormValue: OfferFormValue): void {
    this.housingService.checkIfOfferLimitReached(this.id).pipe(
      tap(limitReached => {
        if (limitReached) {
          this.modalService.toggleOfferLimitReachedModal()
        }
      })
    ).subscribe();
}

À partir de là dans le pipe, on souhaite traiter uniquement le cas où la limite n'est pas atteinte. On peut donc ajouter unfilter:

onSubmitForm(offerFormValue: OfferFormValue): void {
    this.housingService.checkIfOfferLimitReached(this.id).pipe(
      tap(limitReached => {
        if (limitReached) {
          this.modalService.toggleOfferLimitReachedModal()
        }
      }),
      filter(limitReached => !limitReached),
    ).subscribe();
}

Du coup, on sait que toute émission qui arrive sous le filter correspond au cas où on a la possibilité de faire une offre sur le bien. On a besoin, ici, de souscrire à un autre Observable — celui retourné par la méthodemakeOffer. Comment on fait ?

Dans ce cas précis, le choix spécifique d'opérateur n'a pas d'importance (car l'Observable parent n'émet qu'une fois) : la norme dans ce cas est d'utiliserswitchMap:

onSubmitForm(offerFormValue: OfferFormValue): void {
    this.housingService.checkIfOfferLimitReached(this.id).pipe(
      tap(limitReached => {
        if (limitReached) {
          this.modalService.toggleOfferLimitReachedModal()
        }
      }),
      filter(limitReached => !limitReached),
      switchMap(() => this.housingService.makeOffer(this.id, offerFormValue.offer))
    ).subscribe();
}

On va rajouter une dernière étape : on peut aussi réagir à la réponse de cette dernière requête ! On va en profiter pour afficher un modal de succès à l'utilisateur, puis pour le rediriger vers la page liste.

Injectez leRouter, et ajoutez la logique à notre méthode :

private router = inject(Router);
//...
this.housingService.checkIfOfferLimitReached(this.id).pipe(
      tap(limitReached => {
        if (limitReached) {
          this.modalService.toggleOfferLimitReachedModal()
        }
      }),
      filter(limitReached => !limitReached),
      switchMap(() => this.housingService.makeOffer(this.id, offerFormValue.offer)),
      tap(() => this.modalService.toggleOfferSubmittedModal()),
      delay(1500),
      tap(() => this.router.navigate(['/housing'])),
      delay(500),
      tap(() => this.modalService.toggleOfferSubmittedModal())
).subscribe();

Ici je vous propose d'utiliser l'opérateurdelaypour ajouter une pause dans le déroulé des opérations.

Pour vérifier le fonctionnement de la méthode, essayez de faire une offre sur un bien de la liste. Sur tous les biens sauf le dernier, l'offre doit fonctionner :

Un modal de succès, expliquant à l'utilisateur que son offre a bien été soumis
Offer submitted!

Sur le dernier, cependant, le serveur va répondre que la limite d'offres à été atteinte, vous devez voir le message d'avertissement :

Un modal d'erreur qui explique à l'utilisateur que la limite d'offres a été atteinte, et qui lui propose de regarder les autres biens disponibles
Too many offers!

Et voilà ! Grâce à la puissance des Observables, nous avons réussi à implémenter un comportement complexe et entièrement dynamique en réaction aux réponses de nos requêtes HTTP.

À vous de jouer

Contexte

Avant même de laisser l'utilisateur arriver sur la page Make an Offer, vous souhaitez vérifier si le bien a déjà été vendu. Si oui, vous afficherez un modal d'erreur ; sinon, vous laisserez l'utilisateur accéder à la page.

Consigne

Vous allez modifier la méthodeonMakeOfferdePropertyDetailComponent  pour implémenter le comportement souhaité.

Voici les informations dont vous aurez besoin :

  • une requête GET à la route/api/properties/:id/soldpermet de savoir si un bien a été vendu : son type de réponse est{ id: string, sold: boolean }

  • ModalServiceexpose une méthodetoggleSoldModalpour afficher le modal qui prévient l'utilisateur de la vente du bien

Pour tester si votre solution fonctionne, essayez de faire une offre sur l'avant dernier bien…

En résumé

  • La méthodepost()de HttpClient prend l'URL de la requête comme premier argument, et le corps à envoyer comme deuxième argument ;

  • Vous créez une requête composée lorsque la réponse d'une requête est utilisée pour en créer une autre ;

  • Attention à l'asynchrone ! Si une action doit être effectuée après une requête, utilisez des opérateurs commetap()dans lepipede la requête ;

Maintenant que vous savez réagir aux réponses de vos requêtes, dans le prochain chapitre, vous verrez comment utiliser les intercepteurs pour agir sur toutes les requêtes de votre application !

Ever considered an OpenClassrooms diploma?
  • Up to 100% of your training program funded
  • Flexible start date
  • Career-focused projects
  • Individual mentoring
Find the training program and funding option that suits you best