Capítulo 5.6: Visualización de Datos con Blade + Fetch
Tiempo de estudio: 50 minutos
1. Enfoque Híbrido: Lo mejor de dos mundos
Podemos construir una página de dos maneras:
- Renderizado Completo del Lado del Servidor (SSR): Laravel genera todo el HTML, incluyendo la lista de planetas. Para cualquier actualización (eliminación, adición), la página se recarga completamente.
- Renderizado Completo del Lado del Cliente (CSR): Laravel entrega un "shell" HTML vacío, y JavaScript solicita todos los datos a la API y los renderiza en el cliente. (Este es el enfoque de Aplicación de Página Única - SPA).
Nuestra elección es un enfoque híbrido:
- Primera carga (SSR): Laravel entrega inmediatamente la página con la lista de planetas ya preparada. Esto es rápido y bueno para el SEO. El usuario ve el contenido al instante.
- Acciones posteriores (CSR): JavaScript intercepta las acciones del usuario (clics en botones) e interactúa con la API, actualizando solo las partes necesarias de la página, sin una recarga completa.
💡 Analogía espacial:
Al entrar en el puente, se te entrega inmediatamente el mapa de navegación principal, impreso en el Centro de Control de Misión (SSR). Ya lo tienes en tus manos, no hay que esperar. Pero luego activas el "modo en vivo" en tu tableta (CSR), y esta comienza a recibir actualizaciones de los satélites en tiempo real, redibujando objetos en tu mapa.
2. Paso 1: Preparación de la página
Trabajaremos con nuestra página de lista de planetas resources/views/planets/index.blade.php
. Ya es capaz de mostrar los datos pasados desde el controlador. Ahora le añadiremos elementos de control que funcionarán a través de JS.
Añadimos un botón "Actualizar lista" y un contenedor para notificaciones:
<div class="controls">
<h2>Lista de todos los planetas conocidos</h2>
<button id="refresh-btn">Actualizar vía API</button>
</div>
<div id="notification-area" class="notification"></div>
<hr>
{{-- Este div será nuestro contenedor para la actualización dinámica --}}
<div id="planet-list-container" class="planet-list">
{{-- Incluimos la vista "hija" que renderiza la lista inicial --}}
@include('planets.partials.list', ['planets' => $planets])
</div>
Presta atención a @include('planets.partials.list', ...)
. Hemos movido la lógica de visualización de la lista a un archivo separado y reutilizable.
Paso 2: Creación de una vista "parcial" (Partial) reutilizable
Mover las partes repetidas a archivos separados es una buena práctica.
Crea el archivo 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>Sistema solar: {{ $planet->solar_system }}</p>
<p>Diámetro: {{ number_format($planet->size_km, 0, '.', ' ') }} km</p>
<a href="{{ route('planets.show', $planet) }}">Saber más →</a>
<button class="delete-btn" data-id="{{ $planet->id }}" data-url="{{ route('api.planets.destroy', $planet) }}">
Desmantelar
</button>
</div>
@empty
<p>No hay ningún planeta en la base de datos.</p>
@endforelse
- Importante: Ten en cuenta que la URL para el botón de eliminar ahora se genera para la ruta de la API:
route('api.planets.destroy', $planet)
. Para ello, asegúrate de tener un recurso nombrado enroutes/api.php
:Route::apiResource('planets', ...)->name('api.planets');
Paso 3: Escritura de JavaScript para la actualización dinámica
Ahora lo más interesante. Crearemos un JavaScript que, al presionar un botón, solicitará una lista fresca de planetas a la API y la volverá a dibujar.
Crea el archivo public/js/planet-manager.js
y conéctalo en 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');
// Función para mostrar notificaciones
function showNotification(message, isError = false) {
notificationArea.textContent = message;
notificationArea.className = isError ? 'notification error' : 'notification success';
setTimeout(() => {
notificationArea.textContent = '';
notificationArea.className = 'notification';
}, 3000);
}
// Función para dibujar una sola tarjeta de planeta
function createPlanetCardHtml(planet) {
// IMPORTANTE: Generamos el mismo HTML que en nuestra vista parcial
return `
<div class="planet-card" id="planet-card-${planet.id}">
<h3>${planet.name}</h3>
<p>Sistema solar: ${planet.solar_system}</p>
<p>Diámetro: ${new Intl.NumberFormat().format(planet.size_km)} km</p>
<a href="/planets/${planet.id}">Saber más →</a>
<button class="delete-btn" data-id="${planet.id}" data-url="/api/planets/${planet.id}">
Desmantelar (JS)
</button>
</div>
`;
}
// Función para solicitar y volver a dibujar la lista de planetas
async function fetchAndRenderPlanets() {
showNotification('Solicitando datos frescos de los satélites orbitales...');
try {
const response = await fetch('/api/planets', {
headers: { 'Accept': 'application/json' }
});
if (!response.ok) {
throw new Error('Error de red al obtener los datos.');
}
const planets = await response.json(); // Laravel por defecto devolverá { data: [...] } para un recurso paginado
planetListContainer.innerHTML = ''; // Limpiamos la lista antigua
if (planets.data.length === 0) {
planetListContainer.innerHTML = '<p>No hay ningún planeta en la base de datos.</p>';
} else {
planets.data.forEach(planet => {
const cardHtml = createPlanetCardHtml(planet);
planetListContainer.innerHTML += cardHtml;
});
}
showNotification('¡Datos actualizados con éxito!', false);
} catch (error) {
console.error('Error al actualizar la lista de planetas:', error);
showNotification(error.message, true);
}
}
// Adjuntamos el manejador al botón
if (refreshBtn) {
refreshBtn.addEventListener('click', fetchAndRenderPlanets);
}
// Aquí se podría mover la lógica de eliminación del capítulo anterior,
// para que todo el JS esté en un solo lugar.
});
3. Verificación Final
- Inicia el servidor (
php artisan serve
o asegúrate de que Herd esté funcionando). - Recrea la base de datos, si es necesario:
php artisan migrate:fresh --seed
. - Abre la página
/planets
en el navegador.- Deberías ver inmediatamente la lista de planetas generada por el servidor.
- Haz clic en el botón "Actualizar vía API".
- Verás una notificación de carga.
- La lista debería desaparecer por un momento y reaparecer, pero esta vez será generada por JavaScript basándose en los datos obtenidos de la API.
¡Has implementado con éxito el modelo híbrido!
Cuestionario para consolidar
🚀 ¡Felicitaciones por completar el Capítulo 5!
Has recorrido un largo camino desde los fundamentos de Blade hasta la creación de páginas híbridas interactivas. Has aprendido a:
- Crear y usar plantillas y diseños de Blade.
- Organizar rutas web y controladores para operaciones CRUD.
- Proteger formularios web y solicitudes AJAX con tokens CSRF.
- Integrar JavaScript para la interacción dinámica con la API sin recargar la página.
Tu Centro de Control de Vuelo es completamente funcional, seguro e interactivo. Estás listo para la siguiente gran etapa: comparar este enfoque con otros frameworks y aprender las mejores prácticas para producción.