Module 12 – Modales et flash messages
Niveau 5.2 – Laravel Inertia (React & Vue)
Objectif
À la fin de ce module vous saurez :
- Afficher les flash messages (succès, erreur, warning) de façon claire et les intégrer dans le layout (bandeau en haut ou toast), en réutilisant les shared data (module 7).
- Implémenter des modales en état local (confirmation de suppression, détail léger) avec React (useState) et Vue (ref), sans changer l’URL.
- Décider quand utiliser une modale = page Inertia (URL dédiée, partageable, refresh) pour un détail ou une édition en overlay.
Nous donnons des exemples de code : bandeau flash dans le layout, modale de confirmation de suppression (React et Vue), et rappel des bonnes pratiques (accessibilité, fermeture au clic extérieur).
Flash messages – rappel
Les messages flash sont définis côté Laravel dans une redirect : redirect()->with('success', 'Utilisateur créé.'). Ils sont exposés à toutes les pages via HandleInertiaRequests::share() (voir module 7), par exemple flash.success, flash.error. Côté client, vous lisez usePage().props.flash et vous affichez un bandeau ou un toast. Le message n’apparaît qu’une fois (la session le consomme ou il n’est renvoyé qu’une requête). Il est donc important d’afficher le flash dans un endroit visible (souvent le layout) pour que l’utilisateur le voie après une action (création, mise à jour, suppression).
Bandeau flash dans le layout – React
Dans AppLayout.jsx, après la navbar et avant children :
import { usePage } from '@inertiajs/react';
export default function AppLayout({ children }) {
const { props } = usePage();
const flash = props.flash || {};
return (
<div>
<nav>{/* ... */}</nav>
{flash.success && (
<div
role="alert"
className="bg-green-100 border-l-4 border-green-500 text-green-700 p-4"
>
{flash.success}
</div>
)}
{flash.error && (
<div
role="alert"
className="bg-red-100 border-l-4 border-red-500 text-red-700 p-4"
>
{flash.error}
</div>
)}
{flash.warning && (
<div
role="alert"
className="bg-amber-100 border-l-4 border-amber-500 text-amber-700 p-4"
>
{flash.warning}
</div>
)}
<main>{children}</main>
</div>
);
}
- role="alert" : utile pour l’accessibilité (lecteurs d’écran). Vous pouvez aussi ajouter un bouton « Fermer » qui masque le bandeau (état local visible que vous mettez à false au clic), pour que l’utilisateur puisse le faire disparaître avant la prochaine navigation.
Bandeau flash – Vue
Même idée dans AppLayout.vue :
<template>
<div>
<nav>...</nav>
<div
v-if="flash.success"
role="alert"
class="bg-green-100 border-l-4 border-green-500 text-green-700 p-4"
>
{{ flash.success }}
</div>
<div
v-if="flash.error"
role="alert"
class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4"
>
{{ flash.error }}
</div>
<main><slot /></main>
</div>
</template>
<script setup>
import { usePage } from '@inertiajs/vue3';
import { computed } from 'vue';
const page = usePage();
const flash = computed(() => page.props.flash || {});
</script>
Modales – état local (confirmation, détail simple)
Pour une modale de confirmation (« Voulez-vous vraiment supprimer cet élément ? ») ou un détail léger (affichage d’un petit bloc d’infos sans changer de page), garder l’état en local : une variable isOpen (ou showModal) et éventuellement selectedItem. Ouvrir la modale = setState(true) et stocker l’item ; fermer = setState(false). L’URL ne change pas ; la modale est purement UI.
Avantages : simple, pas de requête supplémentaire, pas de gestion d’URL.
Inconvénients : pas de lien direct vers « cette modale » (pas partageable, pas de refresh qui rouvre la modale).
Nous avons déjà vu un exemple de modale de confirmation (suppression) au module 8 (gestion d’état). Rappel rapide en React :
const [showConfirm, setShowConfirm] = useState(false);
const [userToDelete, setUserToDelete] = useState(null);
const openConfirm = (user) => {
setUserToDelete(user);
setShowConfirm(true);
};
const closeConfirm = () => {
setShowConfirm(false);
setUserToDelete(null);
};
// Dans le JSX :
{showConfirm && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center">
<div className="bg-white p-6 rounded shadow-lg">
<p>Supprimer {userToDelete?.name} ?</p>
<button onClick={() => { router.delete(route('users.destroy', userToDelete.id)); closeConfirm(); }}>
Oui
</button>
<button onClick={closeConfirm}>Non</button>
</div>
</div>
)}
En Vue : ref(false) et ref(null) pour showConfirm et userToDelete, même logique. Pour l’accessibilité, vous pouvez gérer le focus (focus trap dans la modale) et la fermeture avec Echap ; des librairies comme @headlessui/react (React) ou @headlessui/vue (Vue) gèrent cela pour vous.
Modales – URL dédiée (page Inertia en overlay)
Pour un détail ou une édition que l’on veut partageable (lien direct, copier-coller de l’URL, refresh qui rouvre la même chose), il vaut mieux que la modale corresponde à une vraie page Inertia : une route Laravel (ex. GET /users/123 ou GET /users/123/edit) qui renvoie Inertia::render('Users/Show', ['user' => $user]). L’utilisateur peut ouvrir cette URL dans un nouvel onglet, la mettre en favori, etc. L’UI peut affecter cette page comme une « modale » (overlay sur la liste) en utilisant le même layout avec une zone modale : quand on est sur /users/123, on affiche la liste en arrière-plan (ou un fond grisé) et le contenu Users/Show dans un panneau ou une modale au centre. Cela se gère plutôt par le routing et le design (une même page full-page peut être stylée en overlay selon le contexte). L’important est que les données et l’URL viennent de Laravel ; pas d’état local pour le contenu de la modale dans ce cas.
À retenir
- Flash : partagé via share(), lu avec usePage().props.flash ; afficher dans le layout (bandeau ou toast) avec role="alert" pour l’accessibilité.
- Modale de confirmation (suppression, etc.) : état local (useState / ref) pour ouvert et élément sélectionné ; pas de changement d’URL.
- Modale = page Inertia : pour un détail ou une édition partageable, utiliser une route et une page Inertia dédiées ; l’URL reflète l’état, le contenu vient des props Laravel.
Dans le prochain module, nous verrons les tests (Laravel assertInertia, tests de composants React/Vue) et les bonnes pratiques (structure, typage, accessibilité).