Avez-vous une question? (216) 97 656 803 hajjriadh@gmail.com
JavaScript

Envoyer un formulaire au serveur avec fetch

Envoyer un formulaire au serveur avec fetch. Dans cette leçon du cours JavaScript, vous apprendrez à : envoyer les données d'un formulaire avec fetch en POST ; choisir entre un corps FormData et un co...

0 chapitres 0 QCM Accès gratuit
Dans cet article

À propos de ce cours

Envoyer un formulaire au serveur avec fetch. Dans cette leçon du cours JavaScript, vous apprendrez à : envoyer les données d'un formulaire avec fetch en POST ; choisir entre un corps FormData et un corps JSON ; gérer l'état d'envoi : bouton désactivé, message, double clic ; traiter le succès et les erreurs sans perdre la saisie.

Chapitre 14 — La gestion des formulaires · 14.7 Envoyer un formulaire au serveur avec fetch

ℹ️ À retenir
Nouvelle leçon : une fois les données vérifiées, il faut les envoyer. La méthode moderne, sans rechargement de la page, utilise fetch et les promesses (chapitre 18). Le TP s'en sert pour son envoi final.
🎯 Objectifs de la leçon
• envoyer les données d'un formulaire avec fetch en POST ;
• choisir entre un corps FormData et un corps JSON ;
• gérer l'état d'envoi : bouton désactivé, message, double clic ;
• traiter le succès et les erreurs sans perdre la saisie.

Séquence d'envoi d'un formulaire avec fetch
Figure 1 — Les étapes d'un envoi asynchrone.

📄 l7_fetch.html

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <form id="avis" action="https://jsonplaceholder.typicode.com/posts" method="post">
    <label>Titre <input name="title" required></label>
    <label>Votre avis <textarea name="body" required rows="3"></textarea></label>
    <button>Publier</button>
  </form>
  <pre id="sortie"></pre>

  <script>
    const form = document.querySelector("#avis");
    const sortie = document.querySelector("#sortie");

    form.addEventListener("submit", async (ev) => {
      ev.preventDefault();
      const bouton = form.querySelector("button");
      bouton.disabled = true;            // empêche le double envoi
      bouton.textContent = "Envoi…";
      sortie.textContent = "";

      try {
        const donnees = Object.fromEntries(new FormData(form));
        const reponse = await fetch(form.action, {   // l'adresse vient de l'attribut action
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(donnees),
        });
        if (!reponse.ok) throw new Error(`Le serveur a répondu ${reponse.status}`);
        const resultat = await reponse.json();
        sortie.textContent = `Avis publié ✔ (numéro ${resultat.id})\n` + JSON.stringify(resultat, null, 2);
        form.reset();                    // vide le formulaire
      } catch (err) {
        sortie.textContent = "Échec de l'envoi : " + err.message + "\nVos saisies sont conservées, réessayez.";
      } finally {
        bouton.disabled = false;         // dans tous les cas
        bouton.textContent = "Publier";
      }
    });
  </script>
</body>
</html>

Pendant l'envoi, le bouton est désactivé et affiche « Envoi… » :

Bouton désactivé pendant l'envoi
Pendant l'attente de la réponse

En cas de succès, le serveur de test jsonplaceholder renvoie les données reçues avec un numéro :

Message de succès après l'envoi
Réponse 201 : formulaire vidé, numéro affiché

En cas d'erreur du serveur (ici une erreur 500 simulée), le message l'indique et les saisies sont conservées :

Message d'erreur, saisie conservée
Réponse 500 : rien n'est perdu

ℹ️ À retenir
Pour nos tests automatiques, le serveur a été simulé (réponses 201 et 500). Avec une connexion Internet, l'exemple fonctionne tel quel avec le vrai service de test jsonplaceholder.typicode.com, qui ne conserve rien.

1. FormData ou JSON ?

// 1) Corps FormData : comme un envoi classique, fichiers compris.
//    Pas d'en-tête Content-Type : le navigateur le met lui-même.
await fetch(url, { method: "POST", body: new FormData(form) });

// 2) Corps JSON : le format préféré des API modernes
await fetch(url, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(objet),
});

Ce n'est pas vous qui choisissez : c'est le serveur qui indique, dans sa documentation, le format qu'il attend.

2. Les bonnes pratiques

  • Désactiver le bouton pendant l'envoi : un double clic enverrait deux fois la commande.
  • Tester reponse.ok: fetch ne rejette sa promesse qu'en cas de problème réseau, pas pour une erreur 400 ou 500.
  • Réactiver dans finally, pour que le bouton revienne même en cas d'erreur.
  • Ne vider le formulaire qu'en cas de succès; en cas d'échec, l'utilisateur ne doit rien avoir à retaper.
  • Afficher les erreurs renvoyées par le serveur (par exemple « cet e-mail est déjà inscrit ») à côté des champs concernés.
  • Ne pas envoyer ce qui est inutile au serveur, comme la confirmation du mot de passe.
ℹ️ À retenir
• submit → preventDefault → validation → fetch(url, { method: "POST", body }).
• Corps FormData (fichiers) ou JSON avec Content-Type: application/json, selon le serveur.
• Bouton désactivé pendant l'envoi, réactivé dans finally; tester reponse.ok.
• Succès : message et form.reset(); échec : message, saisie conservée.

🛠 Activité 7 — Envoyer le formulaire de contact

Mission : Ajoutez l'envoi au formulaire de contact des activités 5 et 6, vers https://jsonplaceholder.typicode.com/posts.

Questions pour avancer (répondez dans votre compte rendu, puis appliquez dans votre projet) :

  1. Envoyez une fois avec un corps FormData, une fois en JSON. Dans l'onglet Réseau (F12), comparez la requête : en-tête Content-Type, contenu du corps.
  2. Cliquez trois fois très vite sur Envoyer, sans désactiver le bouton. Combien de requêtes partent ? Corrigez.
  3. Coupez le réseau (onglet Réseau › Hors ligne) et envoyez. Quel bloc de votre code s'exécute ? Le message est-il compréhensible pour l'utilisateur ?
  4. Envoyez vers une adresse qui n'existe pas (…/postsXYZ) : fetch rejette-t-il la promesse ? Quelle ligne de votre code détecte l'erreur ?
  5. Imaginez que le serveur réponde 400 { "champ": "email", "message": "Adresse déjà utilisée" }. Écrivez le code qui affiche ce message sous le bon champ.
  6. Après un envoi réussi, l'utilisateur appuie sur « Retour » puis « Avancer » dans le navigateur. Le formulaire est-il renvoyé ? Comparez avec un envoi classique sans JavaScript.

Je vérifie / je rends :

  • ☐ Le formulaire avec envoi et une capture de l'onglet Réseau pour chaque format.

← 14.6 La validation de formulaire avec HTML514.8 TP 14 : Validation d'un formulaire en JavaScript →