Cette technique s’applique à tout formulaire Web alors pourquoi un formulaire Bootstrap ? Le cadriciel « Bootstrap 5 » n’offre pas qu’une librairie de styles, c’est aussi des composantes, dont la très pratique « validation de formulaire ». Ce tutoriel propose seulement de combiner la validation Bootstrap 5 avec le traitement de formulaire de façon asynchrone avec « l’API Fetch ».
« L’API Fetch (en anglais, le verbe fetch signifie récupérer) fournit une interface JavaScript pour accéder et manipuler certaines parties du protocole, comme les requêtes et les réponses. Elle fournit également une méthode globale fetch() qui permet un accès pratique aux ressources récupérées de façon asynchrone sur le réseau. À la différence de XMLHttpRequest qui fonctionne à l’aide de fonctions de rappel (callbacks), l’API Fetch utilise les promesses et fournit une meilleure alternative, qui peut être utilisée dans les service workers. L’API Fetch intègre également des concepts HTTP avancés tels que le CORS et d’autres extensions de HTTP.
Source : mdn web doc.
Avertissement
Avant d’aller plus loin, il faut savoir que cette technologie à deux petits défauts! Le premier, il fonctionne en JavaScript... On est loin d’autoriser ou non un fichier témoin (cookies) mais c’est tout de même possible qu’un usager désactive le JavaScript. Si votre formulaire est vital, n’utilisez pas cette technique ou ajouter simplement une adresse (Url) pour taiter le formulaire via l’attribut action="[url]"
de la balise form
. Vous aurez à faire deux codes différents mais ça fonctionne. Si le JavaScript est activé, l’API Fetch traitera votre formulaire, sinon le formulaire sera envoyé normalement via l’adresse (url) du formulaire. L’autre défaut, c’est que les robots n’indexeront pas la page de traitement du formulaire. Ce qui est le dernier de nos soucis! Et d’ailleurs, c’est aussi l’avantage de cette technique, cacher la page qui traite le formulaire aux robots d’indexation! Ce n’est certainement pas à toute épreuve, mais c’est une couche supplémentaire pour sécuriser vos formulaires contre les abus de robots.
Formulaire Bootstrap 5.3.2
Nous allons tout d’abord récupérer un formulaire Bootstrap 5 avec le script de validation qui contourne la validation par défaut des navigateurs. Ce qui est important à ce stade c’est d’ajouter au formulaire la classe « needs-validation » et désactiver la validation par défaut du navigateur avec l’attribut novalidate
. Et de s’assurer que les champs de saisies (input,textarea,checkbox...) aient la classe « form-control » ainsi que l’attribut HTML required
. Noter que Bootstrap valide aussi les champs de type courriel ou nombre... Ensuite, le JavaScript se chargera d’ajouter un écouteur d’évènement (Listener) pour valider tous les formulaires de la page Web.
Optionnel : Vous pouvez aussi ajouter des messages pour signaler un champ bien rempli (avec la classe « valid-feedback » ) ou les champs avec une erreur (avec la classe « invalid-feedback »). Mais ce n’est pas obligatoire...
Noter enfin le ID « oForm » qui permettra d’enlever le formulaire et d’afficher les messages.
<!-- formulaire Bootstrap -->
<div id="oForm">
<form class="row g-3 needs-validation" novalidate>
<div class="col-md-4">
<label for="validationCustom01" class="form-label">Prénom</label>
<input type="text" class="form-control" id="validationCustom01" value="Oz" required>
<div class="valid-feedback">Valide!</div>
</div>
<div class="col-md-4">
<label for="validationCustom02" class="form-label">Nom</label>
<input type="text" class="form-control" id="validationCustom02" value="Nog" required>
<div class="valid-feedback">Valide!</div>
</div>
<div class="col-md-4">
<label for="validationCustomUsername" class="form-label">Nom d’usager</label>
<div class="input-group has-validation">
<span class="input-group-text" id="inputGroupPrepend">@</span>
<input type="text" class="form-control" id="validationCustomUsername" aria-describedby="inputGroupPrepend" required>
<div class="invalid-feedback">Veuillez entrer un nom d’usager.</div>
</div>
</div>
<div class="col-md-6">
<label for="validationCustom03" class="form-label">Ville</label>
<input type="text" class="form-control" id="validationCustom03" required>
<div class="invalid-feedback">Veuillez entrer une ville.</div>
</div>
<div class="col-md-3">
<label for="validationCustom04" class="form-label">Province</label>
<select class="form-select" id="validationCustom04" required>
<option selected disabled value="">Sélectionner...</option>
<option>...</option>
</select>
<div class="invalid-feedback">Veuillez sélectionner une province.</div>
</div>
<div class="col-md-3">
<label for="validationCustom05" class="form-label">Code postal</label>
<input type="text" class="form-control" id="validationCustom05" required>
<div class="invalid-feedback">Veuillez entrer un code postal.</div>
</div>
<div class="col-12">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="invalidCheck" required>
<label class="form-check-label" for="invalidCheck">
Accepter les termes et conditions
</label>
<div class="invalid-feedback">Vous devez accepter les termes et conditions avant de soumettre le formulaire.</div>
</div>
</div>
<div class="col-12">
<button class="btn btn-primary" type="submit">Envoyer le formulaire</button>
</div>
</form>
</div>
<script>
// Désactiver les soumissions de formulaire s'il y a des champs invalides
(() => {
'use strict'
// Récupérez tous les formulaires auxquels nous souhaitons appliquer des styles de validation Bootstrap personnalisés
const forms = document.querySelectorAll('.needs-validation')
// Boucle les formulaire
Array.from(forms).forEach(form => {
// Écouteur sur l’événement « submit » (envoyer)
form.addEventListener('submit', event => {
if (!form.checkValidity()) {
// Le formulaire invalide n’est pas envoyé
event.preventDefault()
event.stopPropagation()
}
// Le formulaire est valide.
form.classList.add('was-validated')
}, false)
})
})()
</script>
Tester ce formulaire, vous devriez obtenir ce résultat :
Formulaire asynchone (fetch)
« Asynchone » veut tout simplement dire sans recharger la page. C’est une requête JavaScript envoyé de façon asynchrone au serveur sans même que l’usager ne s’en rende compte. Et le JavaScript attend ensuite la réponse retournée par le serveur. C’est comme un appel AJAX à la différence que l’API « Fetch » est basée « promise-based », beaucoup plus adapté au Web et plus sécuritaire. C’est-à-dire qui promet éventuellement une réponse peut importe le résultat, même s’il y a une erreur via l’entête HTTP par exemple le script ne plantera pas...
La première chose qu’il faut faire étant donné que tout ce passe en arrière, c’est d’aviser l’usager que le formulaire est envoyé. Comme un pictogramme en boucle qui indique le chargement d’une page, ou un simple message comme cet exemple. Noter que cet exemple ne transmet rien pour l’instant. Je profite de ce premier pas pour ajouter le code qui récupère les valeurs du formulaire « FormData » dans un objet « URLSearchParams ».
<script>
// Désactiver les soumissions de formulaire s'il y a des champs invalides
(() => {
'use strict'
// Récupérez tous les formulaires auxquels nous souhaitons appliquer des styles de validation Bootstrap personnalisés
const forms = document.querySelectorAll('.needs-validation')
// Boucle les formulaire
Array.from(forms).forEach(form => {
// Écouteur sur l’événement « submit » (envoyer)
form.addEventListener('submit', event => {
if (!form.checkValidity()) {
// Le formulaire invalide n’est pas envoyé
event.preventDefault()
event.stopPropagation()
} else {
// Retire le formulaire du conteneur « oForm » et affiche un message indiquant le « chargement... »
document.getElementById("oForm").innerHTML = 'Chargement...';
event.preventDefault()
event.stopPropagation()
// Récupère les données du formulaire
const formData = new URLSearchParams();
for (const pair of new FormData(form)) {
formData.append(pair[0], pair[1]);
}
}
// Le formulaire est valide.
form.classList.add('was-validated')
}, false)
})
})()
</script>
La seule chose que fait ce script est d’afficher un message de chargement, et de récupérer les valeurs du formulaire qui devront être envoyées. Exemple.
Requête avec l’API « Fetch »
C’est ici que ça devient intéressant! Le formulaire n’est pas véritablement traité dans cet exemple, c’est seulement une simulation. Vous pouvez d’ailleurs utiliser votre langage serveur préféré. Il faut seulement savoir que l’API « Fetch » permet de lire une réponse en format JSON. Et donc votre programme côté serveur n’aura qu’à retourner une chaine JSON. La seule et unique règle, un JSON valide. Avec cette méthode on peut récupérer le résultat complet d’une recherche par exemple. Et j’utilise ici le format JSON par ce que c’est pratique, mais vous pourriez utiliser du HTML, du XML etc.
Par exemple dans le cas d’un succès. Noter la valeur de la réponse « ok »" et le message « Succès, le formulaire a bien été envoyé! » que notre script va récupérer :
{"date": "16 février 2024","Neural": "Propulsé par les Trucsweb.com","version": "twFetchCourriel v1.0","reponse":"ok","mess":"Succès, le formulaire a bien été envoyé!"}
Exemple d’erreur :
{"date": "16 février 2024","Neural": "Propulsé par les Trucsweb.com","version": "twFetchCourriel v1.0","reponse":"erreur","mess":"Erreur, le formulaire n’a pas été envoyé!"}
Donc dans ce script, l’important c’est l’adresse de la page demandée par le « Fetch », soit le sous-dossier « /succes/ ». Pour la cause de la simulation, j’ai deux scripts, un qui demande une simulation de page de succès et un autre qui demande la simulation d’une page avec erreur :
<script>
// Désactiver les soumissions de formulaire s'il y a des champs invalides
(() => {
'use strict'
// Récupérez tous les formulaires auxquels nous souhaitons appliquer des styles de validation Bootstrap personnalisés
const forms = document.querySelectorAll('.needs-validation')
// Boucle les formulaire
Array.from(forms).forEach(form => {
// Écouteur sur l’événement « submit » (envoyer)
form.addEventListener('submit', event => {
if (!form.checkValidity()) {
// Le formulaire invalide n’est pas envoyé
event.preventDefault()
event.stopPropagation()
} else {
// Retire le formulaire du conteneur « oForm » et affiche un message indiquant le « chargement... »
document.getElementById("oForm").innerHTML = 'Chargement...';
event.preventDefault()
event.stopPropagation()
// Récupère les données du formulaire
const formData = new URLSearchParams();
for (const pair of new FormData(form)) {
formData.append(pair[0], pair[1]);
}
// Fait la requête vers le sous-dossier success.
fetch('succes/', { // Exemple de succès
method: 'POST',
credentials: "same-origin",
// Passage des valeur du formulaire de l’objet « formData »
body: formData
}).then(function (response) {
if (response.ok) {return response.json();}
// S’il n’y a pas d’erreur de la demande Fetch, retourne la « promesse »
return Promise.reject(response);
}).then(function (data) {
// Et on récupère la réponse JSON dans l’objet « data »
// Trace dans la console
console.log(data);
if (data.reponse == "ok") {
// Message de succès si le formulaire a été traité
document.getElementById("oForm").innerHTML = '<div class="alert alert-success" role="alert">'+data.mess+'</div>';
} else {
// Message d'erreur si le formulaire n'a pas été traité
document.getElementById("oForm").innerHTML = '<div class="alert alert-danger" role="alert">'+data.mess+'</div>';
}
// Optionnelle, permet de défiler la page en haut du formulaire (oForm) pour voir le message
window.location = (''+window.location).replace(/#[A-Za-z0-9_]*$/,'')+'#oForm';
}).catch(function (error) {
// Affiche le message d'erreur s'il y a lieu
console.warn(error);
});
}
// Le formulaire est valide.
form.classList.add('was-validated')
}, false)
})
})()
</script>
Code complet
Conclusion
Cette méthode est utilisée ici avec un formulaire, mais l’API « Fetch » peut être utilisée pour n’importe quoi! Un résultat de recherche comme je disais plus haut, un contenu dynamique, etc. En fait, chaque page du site peut être générée de cette façon. Mais n’oubliez pas que le référencement d’un tel contenu sera difficile, voire impossible, sans un accès direct et conventionnel au contenu.
Références
- Bootstrap v5.3 par Bootstrap
- Validation · Bootstrap v5.3 par Bootstrap
- Les « cadre de travail CSS » ou cadriciel (Framework) par Django Blais - trucsweb.com
- Utiliser l’API Fetch par MDN web docs
- Sécuriser un formulaire de contact au Far-Web! par Django Blais - trucsweb.com