À propos de ce cours
Chapitre 14 — La gestion des formulaires · 14.7 Envoyer un formulaire au serveur avec fetch
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.• 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.

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… » :

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 :

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 :

Réponse 500 : rien n'est perdu
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:fetchne 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.
•
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) :
- Envoyez une fois avec un corps
FormData, une fois en JSON. Dans l'onglet Réseau (F12), comparez la requête : en-têteContent-Type, contenu du corps. - Cliquez trois fois très vite sur Envoyer, sans désactiver le bouton. Combien de requêtes partent ? Corrigez.
- 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 ?
- Envoyez vers une adresse qui n'existe pas (
…/postsXYZ) :fetchrejette-t-il la promesse ? Quelle ligne de votre code détecte l'erreur ? - 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. - 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 →