Skip to content

Chapitre 5.6 : Affichage des données via Blade + Fetch

Temps d'étude : 50 minutes


1. Approche hybride : Le meilleur des deux mondes

Nous pouvons construire une page de deux manières :

  1. Rendu côté serveur complet (SSR) : Laravel génère tout le HTML, y compris la liste des planètes. Pour toute mise à jour (suppression, ajout), la page est entièrement rechargée.
  2. Rendu côté client complet (CSR) : Laravel renvoie une "coque" HTML vide, et JavaScript demande toutes les données à l'API et les rend côté client. (C'est l'approche Single Page Application - SPA).

Notre choix — l'approche hybride :

  • Premier chargement (SSR) : Laravel renvoie immédiatement la page avec une liste de planètes déjà prête. C'est rapide et bon pour le SEO. L'utilisateur voit immédiatement le contenu.
  • Actions suivantes (CSR) : JavaScript intercepte les actions de l'utilisateur (clics sur les boutons) et interagit avec l'API, ne mettant à jour que les parties nécessaires de la page, sans rechargement complet.

💡 Analogie spatiale :

En entrant sur la passerelle, on vous remet immédiatement une carte de navigation principale, imprimée au centre de contrôle (SSR). Vous l'avez déjà en main, pas besoin d'attendre. Mais ensuite, vous activez le "mode direct" sur votre tablette (CSR), et elle commence à recevoir des mises à jour des satellites en temps réel, redessinant les objets sur votre carte.


2. Étape 1 : Préparation de la page

Nous allons travailler avec notre page de liste des planètes resources/views/planets/index.blade.php. Elle sait déjà afficher les données transmises par le contrôleur. Nous allons maintenant y ajouter des éléments de contrôle qui fonctionneront via JS.

Ajoutons le bouton "Actualiser la liste" et un conteneur pour les notifications :

    <div class="controls">
        <h2>Liste de toutes les planètes connues</h2>
        <button id="refresh-btn">Actualiser via API</button>
    </div>
    <div id="notification-area" class="notification"></div>
    <hr>
    {{-- Ce div sera notre conteneur pour la mise à jour dynamique --}}
    <div id="planet-list-container" class="planet-list">
        {{-- Inclut la vue "enfant" qui rend la liste initiale --}}
        @include('planets.partials.list', ['planets' => $planets])
    </div>

Notez bien @include('planets.partials.list', ...). Nous avons extrait la logique d'affichage de la liste dans un fichier séparé et réutilisable.


Étape 2 : Création d'une vue "partielle" réutilisable (Partial)

Extraire les parties répétitives dans des fichiers séparés est une bonne pratique.

Créez le fichier resources/views/planets/partials/list.blade.php :

@forelse($planets as $planet)
    <div class="planet-card" id="planet-card-{{ $planet->id }}">
        <h3>{{ $planet->name }}</h3>
        <p>Système solaire : {{ $planet->solar_system }}</p>
        <p>Diamètre : {{ number_format($planet->size_km, 0, '.', ' ') }} km</p>
        <a href="{{ route('planets.show', $planet) }}">En savoir plus &rarr;</a>
        <button class="delete-btn" data-id="{{ $planet->id }}" data-url="{{ route('api.planets.destroy', $planet) }}">
            Désaffecter
        </button>
    </div>
@empty
    <p>Il n'y a aucune planète dans la base de données.</p>
@endforelse
  • Important : Notez que l'URL du bouton de suppression est maintenant générée pour la route API : route('api.planets.destroy', $planet). Pour cela, assurez-vous d'avoir une ressource nommée dans routes/api.php : Route::apiResource('planets', ...)->name('api.planets');

Étape 3 : Écriture du JavaScript pour la mise à jour dynamique

Maintenant, la partie la plus intéressante. Créons un JavaScript qui, via un bouton, demandera une liste de planètes actualisée à l'API et la redessinera.

Créez le fichier public/js/planet-manager.js et incluez-le dans layouts/app.blade.php.

document.addEventListener('DOMContentLoaded', () => {
    const refreshBtn = document.getElementById('refresh-btn');
    const planetListContainer = document.getElementById('planet-list-container');
    const notificationArea = document.getElementById('notification-area');

    // Fonction pour afficher les notifications
    function showNotification(message, isError = false) {
        notificationArea.textContent = message;
        notificationArea.className = isError ? 'notification error' : 'notification success';
        setTimeout(() => {
            notificationArea.textContent = '';
            notificationArea.className = 'notification';
        }, 3000);
    }

    // Fonction pour dessiner une seule carte de planète
    function createPlanetCardHtml(planet) {
        // IMPORTANT : Nous générons le même HTML que dans notre vue partielle
        return `
            <div class="planet-card" id="planet-card-${planet.id}">
                <h3>${planet.name}</h3>
                <p>Système solaire : ${planet.solar_system}</p>
                <p>Diamètre : ${new Intl.NumberFormat().format(planet.size_km)} km</p>
                <a href="/planets/${planet.id}">En savoir plus &rarr;</a>
                <button class="delete-btn" data-id="${planet.id}" data-url="/api/planets/${planet.id}">
                    Décommissionner (JS)
                </button>
            </div>
        `;
    }

    // Fonction pour demander et redessiner la liste des planètes
    async function fetchAndRenderPlanets() {
        showNotification('Requête de données fraîches auprès des satellites orbitaux...');
        try {
            const response = await fetch('/api/planets', {
                headers: { 'Accept': 'application/json' }
            });

            if (!response.ok) {
                throw new Error('Erreur réseau lors de la récupération des données.');
            }

            const planets = await response.json(); // Laravel retournera par défaut { data: [...] } pour une ressource paginée

            planetListContainer.innerHTML = ''; // Nettoyage de l'ancienne liste

            if (planets.data.length === 0) {
                planetListContainer.innerHTML = '<p>Il n\'y a aucune planète dans la base de données.</p>';
            } else {
                planets.data.forEach(planet => {
                    const cardHtml = createPlanetCardHtml(planet);
                    planetListContainer.innerHTML += cardHtml;
                });
            }
            showNotification('Données mises à jour avec succès !', false);
        } catch (error) {
            console.error('Erreur lors de la mise à jour de la liste des planètes :', error);
            showNotification(error.message, true);
        }
    }

    // Attacher le gestionnaire au bouton
    if (refreshBtn) {
        refreshBtn.addEventListener('click', fetchAndRenderPlanets);
    }

    // La logique de suppression du chapitre précédent pourra être déplacée ici,
    // afin que tout le JS soit au même endroit.
});

3. Vérification finale

  1. Lancez le serveur (php artisan serve ou assurez-vous que Herd fonctionne).
  2. Recréez la base de données, si nécessaire : php artisan migrate:fresh --seed.
  3. Ouvrez la page /planets dans le navigateur.
    • Vous devriez voir immédiatement la liste des planètes, générée par le serveur.
  4. Cliquez sur le bouton "Actualiser via API".
    • Vous verrez une notification de chargement.
    • La liste devrait disparaître un instant et réapparaître, mais cette fois-ci, elle sera générée par JavaScript à partir des données reçues de l'API.

Vous avez implémenté avec succès un modèle hybride !


Quiz pour la consolidation

1. Qu'est-ce que l'approche hybride de rendu (SSR + CSR) ?

2. Quel est l'avantage principal du rendu côté serveur initial (SSR) ?

3. Pourquoi `@include('planets.partials.list')` est-il utilisé dans l'exemple ?

4. Dans le code JavaScript, nous dupliquons la structure HTML de la carte. Quel pourrait être un moyen plus avancé d'éviter cela ?

5. Pourquoi est-il important que l'API (`/api/planets`) et le code JavaScript (`createPlanetCardHtml`) génèrent des données/HTML cohérents ?


🚀 Félicitations pour avoir terminé le Chapitre 5 !

Vous avez parcouru un long chemin, des bases de Blade à la création de pages hybrides interactives. Vous avez appris à :

  • Créer et utiliser des modèles et des mises en page Blade.
  • Organiser des routes web et des contrôleurs pour les opérations CRUD.
  • Protéger les formulaires web et les requêtes AJAX à l'aide de jetons CSRF.
  • Intégrer JavaScript pour une interaction dynamique avec l'API sans recharger la page.

Votre Centre de contrôle de vol est entièrement fonctionnel, sécurisé et interactif. Vous êtes prêt pour la prochaine grande étape : comparer cette approche avec d'autres frameworks et explorer les meilleures pratiques pour la production.