Gérer les réponses AJAX dans un module PrestaShop : validation et UX
Apprenez à gérer proprement les réponses AJAX dans un module PrestaShop : validation côté serveur, affichage conditionnel des erreurs et contrôle du bouton submit.
En bref : Pour gérer différentes actions dans le callback success d'une requête AJAX PrestaShop, structurez votre réponse PHP en JSON avec un champ booléen success et un objet errors associatif, puis branchez votre logique JavaScript (désactivation du bouton, affichage ciblé des erreurs) en fonction de ce flag.
Introduction
Lorsqu'on développe un module PrestaShop avec un formulaire côté front-office, la gestion AJAX des soumissions est incontournable pour offrir une expérience utilisateur fluide. Un point technique revient souvent : comment différencier les cas de succès et d'erreur dans le callback success de jQuery, afin d'adapter le comportement de l'interface — par exemple désactiver le bouton d'envoi uniquement après un envoi réussi, tout en affichant les erreurs de validation sans bloquer le formulaire.
Cet article détaille l'architecture complète d'un formulaire AJAX dans un module PrestaShop, de la structure JSON côté PHP au traitement conditionnel côté JavaScript.
Architecture de la réponse JSON côté PHP
Le principe fondamental est de renvoyer un objet JSON structuré depuis le fichier PHP de traitement AJAX. Cette réponse doit contenir au minimum deux informations : un indicateur de succès et les messages associés.
Structure recommandée
<?php
// modules/monmodule/ajax.php
require_once dirname(__FILE__) . '/../../config/config.inc.php';
require_once dirname(__FILE__) . '/../../init.php';
// Vérification du token CSRF (obligatoire en 8.x)
if (!Tools::getToken(false)) {
die(json_encode([
'success' => false,
'errors' => ['security' => 'Token de sécurité invalide.']
]));
}
if (Tools::getValue('ajax') === 'true') {
$errors = [];
$email = Tools::getValue('from');
$tel = Tools::getValue('tel');
$idContact = (int) Tools::getValue('id_contact');
// Validation email
if (empty($email) || !Validate::isEmail($email)) {
$errors['from'] = 'Veuillez saisir une adresse email valide.';
}
// Validation téléphone
if (empty($tel) || !Validate::isPhoneNumber($tel)) {
$errors['tel'] = 'Veuillez saisir un numéro de téléphone valide.';
}
// Validation contact
if ($idContact <= 0) {
$errors['id_contact'] = 'Veuillez sélectionner un contact.';
}
if (!empty($errors)) {
die(json_encode([
'success' => false,
'errors' => $errors
]));
}
// Traitement métier (envoi email, insertion BDD, etc.)
// ...
die(json_encode([
'success' => true,
'message' => 'Votre demande a bien été envoyée. Nous vous recontactons rapidement.'
]));
}
Le point clé : le champ success (booléen) permet au JavaScript de brancher sa logique. Le champ errors est un objet associatif où chaque clé correspond à un champ du formulaire, ce qui facilite l'affichage ciblé des erreurs.
Utiliser les classes Validate de PrestaShop
PrestaShop fournit la classe Validate avec des dizaines de méthodes statiques prêtes à l'emploi. Utilisez-les systématiquement plutôt que des regex maison :
- `Validate::isEmail($value)` — validation email
- `Validate::isPhoneNumber($value)` — numéro de téléphone
- `Validate::isCleanHtml($value)` — protection XSS
- `Validate::isGenericName($value)` — noms sans caractères spéciaux dangereux
Traitement conditionnel en JavaScript
Côté front-end, le callback success de $.ajax se déclenche dès que le serveur renvoie une réponse HTTP 200, quel que soit le contenu. C'est pourquoi il faut tester le champ success du JSON pour différencier un envoi réussi d'une erreur de validation.
Implémentation complète
$(document).ready(function () {
var $form = $('.callback');
var $submitBtn = $form.find('[type="submit"]');
var $result = $('#result');
$form.on('submit', function (e) {
e.preventDefault();
// Nettoyage des erreurs précédentes
$result.empty();
$form.find('.field-error').removeClass('field-error');
$.ajax({
type: 'POST',
url: baseDir + 'modules/monmodule/ajax.php',
data: $form.serialize() + '&ajax=true',
dataType: 'json',
beforeSend: function () {
$submitBtn.prop('disabled', true).text('Envoi en cours...');
},
success: function (response) {
if (response.success) {
// Succès : afficher le message et verrouiller le formulaire
$result
.removeClass('alert-danger')
.addClass('alert alert-success')
.html(response.message);
$submitBtn.prop('disabled', true).text('Message envoyé');
} else {
// Erreurs de validation : afficher et réactiver le bouton
$submitBtn.prop('disabled', false).text('Envoyer');
var errorHtml = '<ul>';
$.each(response.errors, function (field, message) {
errorHtml += '<li>' + message + '</li>';
// Surbrillance du champ en erreur
$('#' + field).addClass('field-error');
});
errorHtml += '</ul>';
$result
.removeClass('alert-success')
.addClass('alert alert-danger')
.html(errorHtml);
}
},
error: function () {
$submitBtn.prop('disabled', false).text('Envoyer');
$result
.addClass('alert alert-danger')
.html('Une erreur technique est survenue. Veuillez réessayer.');
}
});
});
});
Points techniques importants
e.preventDefault() vs return false : Les deux empêchent la soumission classique du formulaire, mais e.preventDefault() est préférable. return false appelle également stopPropagation(), ce qui peut bloquer d'autres écouteurs d'événements (modules analytics, hooks JavaScript de PrestaShop).
$form.serialize() plutôt que la concaténation manuelle : la sérialisation automatique est plus fiable, gère l'encodage URL des caractères spéciaux et s'adapte si vous ajoutez des champs au formulaire.
.prop('disabled', true) vs .attr('disabled', true) : Depuis jQuery 1.6, .prop() est la méthode correcte pour les propriétés booléennes comme disabled, checked ou selected.
Adaptation pour PrestaShop 8.x
Sur PrestaShop 8.x, plusieurs ajustements sont nécessaires par rapport aux versions 1.6/1.7.
Utiliser un contrôleur front plutôt qu'un fichier PHP direct
L'accès direct à un fichier PHP dans le dossier du module (modules/monmodule/ajax.php) fonctionne mais n'est pas la bonne pratique sur PS 8.x. Préférez un ModuleFrontController :
<?php
// modules/monmodule/controllers/front/ajax.php
class MonModuleAjaxModuleFrontController extends ModuleFrontController
{
public function initContent()
{
// Désactiver le rendu du template
$this->ajax = true;
parent::initContent();
}
public function displayAjax()
{
$errors = [];
$email = Tools::getValue('from');
$tel = Tools::getValue('tel');
if (empty($email) || !Validate::isEmail($email)) {
$errors['from'] = 'Adresse email invalide.';
}
if (empty($tel) || !Validate::isPhoneNumber($tel)) {
$errors['tel'] = 'Numéro de téléphone invalide.';
}
if (!empty($errors)) {
$this->ajaxRender(json_encode([
'success' => false,
'errors' => $errors
]));
return;
}
// Traitement...
$this->ajaxRender(json_encode([
'success' => true,
'message' => 'Demande envoyée avec succès.'
]));
}
}
L'URL AJAX côté JavaScript devient alors :
url: prestashop.urls.base_url + 'module/monmodule/ajax'
Sécurité CSRF avec les tokens
Sur PrestaShop 8.x, pensez à inclure le token dans vos requêtes AJAX :
data: $form.serialize() + '&ajax=true&token=' + prestashop.static_token,
JavaScript sans jQuery
PrestaShop 8.x charge encore jQuery, mais si vous développez un module tourné vers l'avenir, voici l'équivalent en JavaScript vanilla avec fetch :
document.addEventListener('DOMContentLoaded', function () {
const form = document.querySelector('.callback');
const submitBtn = form.querySelector('[type="submit"]');
const resultDiv = document.getElementById('result');
form.addEventListener('submit', async function (e) {
e.preventDefault();
resultDiv.innerHTML = '';
submitBtn.disabled = true;
submitBtn.textContent = 'Envoi en cours...';
try {
const formData = new FormData(form);
formData.append('ajax', 'true');
const response = await fetch(
prestashop.urls.base_url + 'module/monmodule/ajax',
{ method: 'POST', body: formData }
);
const data = await response.json();
if (data.success) {
resultDiv.className = 'alert alert-success';
resultDiv.textContent = data.message;
} else {
submitBtn.disabled = false;
submitBtn.textContent = 'Envoyer';
resultDiv.className = 'alert alert-danger';
resultDiv.innerHTML = Object.values(data.errors)
.map(msg => '<p>' + msg + '</p>')
.join('');
}
} catch (err) {
submitBtn.disabled = false;
submitBtn.textContent = 'Envoyer';
resultDiv.className = 'alert alert-danger';
resultDiv.textContent = 'Erreur technique. Veuillez réessayer.';
}
});
});
Bonnes pratiques UX pour les formulaires AJAX
Feedback visuel immédiat
Désactivez le bouton dès le clic (beforeSend) pour éviter les doubles soumissions, mais réactivez-le en cas d'erreur de validation. Le texte du bouton doit refléter l'état : « Envoyer » → « Envoi en cours... » → « Message envoyé » ou retour à « Envoyer ».
Affichage ciblé des erreurs
Plutôt qu'un message d'erreur global, associez chaque erreur au champ concerné. Le CSS minimal pour la surbrillance :
.field-error {
border-color: #e74c3c !important;
box-shadow: 0 0 0 2px rgba(231, 76, 60, 0.2);
}
.alert {
padding: 12px 16px;
border-radius: 4px;
margin-top: 10px;
}
.alert-success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.alert-danger { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
Gestion du callback `error`
N'oubliez jamais le callback error de $.ajax. Sans lui, une erreur réseau ou serveur (timeout, 500) laissera l'utilisateur face à un bouton désactivé sans explication.
Erreurs courantes à éviter
- **Oublier `dataType: 'json'`** : sans cette option, jQuery ne parse pas automatiquement la réponse et `jsonData` sera une chaîne brute.
- **Concaténer les données manuellement** : `'data=true&from=' + val` est fragile. `$form.serialize()` gère l'encodage et évolue avec le formulaire.
- **Appeler `die()` sans `header('Content-Type: application/json')`** : certains navigateurs peuvent mal interpréter la réponse. Ajoutez toujours l'en-tête.
- **Mélanger `html()` pour succès et erreurs dans le même conteneur** sans réinitialiser les classes CSS : l'utilisateur voit un message de succès stylé en rouge, ou inversement.
Questions fréquentes
Tout ce que vous devez savoir sur ce sujet.
Un projet PrestaShop ?
Discutons-en directement.
193 projets livrés
Lire sur le blog

Alexandre Carette
Expert PrestaShop & Architecture E-commerce
Développeur PrestaShop depuis 2014, 193 projets livrés. Je conçois des architectures headless Nuxt + PrestaShop et des outils d'automatisation IA pour les e-commerçants.