Chapter 5.6: Displaying Data via Blade + Fetch
Study Time: 50 minutes
1. Hybrid Approach: The Best of Both Worlds
We can build a page in two ways:
- Full Server-Side Rendering (SSR): Laravel generates the entire HTML, including the list of planets. For any update (deletion, addition), the page reloads completely.
- Full Client-Side Rendering (CSR): Laravel serves an empty HTML "shell," and JavaScript requests all data from the API and renders it on the client. (This is the Single Page Application - SPA - approach).
Our choice is a hybrid approach:
- Initial Load (SSR): Laravel immediately serves the page with the list of planets already rendered. This is fast and good for SEO. The user sees the content instantly.
- Subsequent Actions (CSR): JavaScript intercepts user actions (button clicks) and interacts with the API, updating only the necessary parts of the page without a full reload.
💡 Space Analogy:
When you enter the bridge, you are immediately given the main navigation map, printed at Mission Control (SSR). It's already in your hands; no need to wait. But then you activate "live mode" on your tablet (CSR), and it starts receiving real-time updates from satellites, redrawing objects on your map.
2. Step 1: Preparing the Page
We will work with our planet list page resources/views/planets/index.blade.php
. It already knows how to display data passed from the controller. Now we will add controls to it that will work via JS.
Add an "Update List" button and a container for notifications:
<div class="controls">
<h2>List of all known planets</h2>
<button id="refresh-btn">Update via API</button>
</div>
<div id="notification-area" class="notification"></div>
<hr>
{{-- This div will be our container for dynamic updates --}}
<div id="planet-list-container" class="planet-list">
{{-- Include the "child" view that renders the initial list --}}
@include('planets.partials.list', ['planets' => $planets])
</div>
Note the @include('planets.partials.list', ...)
. We have moved the list display logic into a separate, reusable file.
Step 2: Creating a Reusable "Partial" View
Extracting repetitive parts into separate files is good practice.
Create the file 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>Solar System: {{ $planet->solar_system }}</p>
<p>Diameter: {{ number_format($planet->size_km, 0, '.', ' ') }} km</p>
<a href="{{ route('planets.show', $planet) }}">Learn more →</a>
<button class="delete-btn" data-id="{{ $planet->id }}" data-url="{{ route('api.planets.destroy', $planet) }}">
Decommission
</button>
</div>
@empty
<p>There are no planets in the database.</p>
@endforelse
- Important: Note that the URL for the delete button is now generated for the API route:
route('api.planets.destroy', $planet)
. To do this, make sure you have a named resource inroutes/api.php
:Route::apiResource('planets', ...)->name('api.planets');
Step 3: Writing JavaScript for Dynamic Updates
Now for the interesting part. Let's create a JavaScript that will request a fresh list of planets from the API at the click of a button and redraw it.
Create the file public/js/planet-manager.js
and include it in 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');
// Function to show notifications
function showNotification(message, isError = false) {
notificationArea.textContent = message;
notificationArea.className = isError ? 'notification error' : 'notification success';
setTimeout(() => {
notificationArea.textContent = '';
notificationArea.className = 'notification';
}, 3000);
}
// Function to render a single planet card HTML
function createPlanetCardHtml(planet) {
// IMPORTANT: We generate the same HTML as in our partial view
return `
<div class="planet-card" id="planet-card-${planet.id}">
<h3>${planet.name}</h3>
<p>Solar System: ${planet.solar_system}</p>
<p>Diameter: ${new Intl.NumberFormat().format(planet.size_km)} km</p>
<a href="/planets/${planet.id}">Learn more →</a>
<button class="delete-btn" data-id="${planet.id}" data-url="/api/planets/${planet.id}">
Decommission (JS)
</button>
</div>
`;
}
// Function to fetch and redraw the list of planets
async function fetchAndRenderPlanets() {
showNotification('Requesting fresh data from orbital satellites...');
try {
const response = await fetch('/api/planets', {
headers: { 'Accept': 'application/json' }
});
if (!response.ok) {
throw new Error('Network error when receiving data.');
}
const planets = await response.json(); // Laravel will return { data: [...] } by default for a paginated resource
planetListContainer.innerHTML = ''; // Clear the old list
if (planets.data.length === 0) {
planetListContainer.innerHTML = '<p>There are no planets in the database.</p>';
} else {
planets.data.forEach(planet => {
const cardHtml = createPlanetCardHtml(planet);
planetListContainer.innerHTML += cardHtml;
});
}
showNotification('Data updated successfully!', false);
} catch (error) {
console.error('Error updating planet list:', error);
showNotification(error.message, true);
}
}
// Add an event listener to the button
if (refreshBtn) {
refreshBtn.addEventListener('click', fetchAndRenderPlanets);
}
// You can move the deletion logic from the previous chapter here,
// so that all the JS is in one place.
});
3. Final Check
- Start the server (
php artisan serve
or make sure Herd is running). - Recreate the database, if necessary:
php artisan migrate:fresh --seed
. - Open the
/planets
page in your browser.- You should immediately see a list of planets generated by the server.
- Click the "Update via API" button.
- You will see a loading notification.
- The list should disappear for a moment and reappear, but this time it will be generated by JavaScript based on data received from the API.
You have successfully implemented a hybrid model!
Reinforcement Quiz
🚀 Congratulations on completing Chapter 5!
You have come a long way from the basics of Blade to creating interactive hybrid pages. You have learned to:
- Create and use Blade templates and layouts.
- Organize web routes and controllers for CRUD operations.
- Protect web forms and AJAX requests with CSRF tokens.
- Integrate JavaScript for dynamic interaction with the API without reloading the page.
Your Mission Control Center is fully functional, secure, and interactive. You are ready for the next big stage — comparing this approach with other frameworks and learning best practices for production.