Skip to content

Capítulo 5.3: Incrustación de JavaScript en Vistas de Laravel

Tiempo de estudio: 50 minutos


1. Dos enfoques de JavaScript en la web

Hasta ahora hemos trabajado con Server-Side Rendering (SSR) — el servidor (Laravel) generaba el HTML listo (a través de Blade) y lo enviaba al navegador. Esto es excelente para el SEO y una carga inicial rápida.

Ahora añadiremos Client-Side Interactions — el navegador, una vez cargada la página, ejecutará código JavaScript para:

  • Enviar solicitudes a nuestra API sin recargar la página.
  • Actualizar dinámicamente partes de la página (por ejemplo, añadir un nuevo planeta a la lista).
  • Mostrar notificaciones y ventanas modales.

💡 Analogía espacial:

Imagine que SSR es recibir un mapa completo de un sistema estelar, impreso en el Centro de Control de Misiones (servidor). Usted ve todos los objetos en el momento de la impresión.

Client-Side JS es su tableta personal (navegador), que se comunica en tiempo real con los satélites (API) y actualiza la posición de los objetos en su mapa, sin solicitar un nuevo mapa de papel del Centro de Control de Misiones.


2. Dónde almacenar y cómo incluir código JS

Como ya hemos descubierto, todos los recursos públicos (CSS, JS, imágenes) deben ubicarse en la carpeta public.

Estructura correcta:

  1. Archivos fuente: Todo su código JS principal debe estar en public/js/. Por ejemplo, public/js/planets.js.
  2. Inclusión en Blade: Utilice el ayudante asset() para generar la URL correcta.

Ejemplo de inclusión en la plantilla layouts/app.blade.php:

<!DOCTYPE html>
<html>
<head>
    {{-- ... --}}
</head>
<body>
    {{-- ... header y main ... --}}

    <footer>
        <p>&copy; {{ date('Y') }} Space Command.</p>
    </footer>

    {{-- Es mejor incluir los scripts al final del body para acelerar el renderizado de la página --}}
    <script src="{{ asset('js/planets.js') }}"></script>
    @stack('scripts') {{-- Creamos un "slot" para scripts de una página específica --}}
</body>
</html>

  • @stack('scripts') — esta es una potente directiva de Blade. Permite a las vistas hijas "empujar" su propio código JS a esta ubicación. Esto es útil cuando una página necesita un script único, y otra no.

3. Ejemplo: Botón "Eliminar" con confirmación

Vamos a añadir un botón "Eliminar" a la página de la lista de planetas (planets/index.blade.php) para cada planeta, que funcionará a través de JavaScript y la API Fetch.

Paso 1: Añadir el botón en resources/views/planets/index.blade.php

Modifique la tarjeta del planeta, añadiendo un botón con atributos de datos:

{{-- ... dentro del bucle @forelse ... --}}
<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="/planets/{{ $planet->id }}">Saber más &rarr;</a>
    <button class="delete-btn" data-id="{{ $planet->id }}" data-url="/api/planets/{{ $planet->id }}">
        Dar de baja
    </button>
</div>
<!-- ... Antes de la etiqueta de cierre del body ... -->
<script src="{{ asset('js/planets.js') }}" defer></script>

  • id="planet-card-{{ $planet->id }}" — un ID único para toda la tarjeta, para que podamos eliminarla del DOM.
  • data-id y data-url — una forma conveniente de pasar datos de PHP (Blade) a JavaScript.

Paso 2: Escribir la lógica JavaScript

Cree el archivo public/js/planets.js y añada el siguiente código:

document.addEventListener('DOMContentLoaded', () => {
    // Encontrar todos los botones "Eliminar"
    const deleteButtons = document.querySelectorAll('.delete-btn');

    deleteButtons.forEach(button => {
        button.addEventListener('click', async (event) => {
            const planetId = event.target.dataset.id;
            const apiUrl = event.target.dataset.url;

            if (!confirm(`¿Está seguro de que desea dar de baja el planeta con ID ${planetId}? Esta acción es irreversible.`)) {
                return; // El usuario hizo clic en "Cancelar"
            }

            try {
                // Enviar una solicitud DELETE a nuestra API
                const response = await fetch(apiUrl, {
                    method: 'DELETE',
                    headers: {
                        'Accept': 'application/json',
                        // Más tarde añadiremos el token CSRF aquí
                    }
                });

                if (response.status === 204) { // 204 No Content - eliminación exitosa
                    // Eliminar la tarjeta del planeta de la página
                    const cardToRemove = document.getElementById(`planet-card-${planetId}`);
                    if (cardToRemove) {
                        cardToRemove.remove();
                    }
                    alert('El planeta ha sido dado de baja exitosamente.');
                } else {
                    // Si la API devolvió un error
                    const errorData = await response.json();
                    alert(`Error: ${errorData.message || 'No se pudo eliminar el planeta.'}`);
                }
            } catch (error) {
                console.error('Error al enviar la solicitud:', error);
                alert('Ocurrió un error de red. Inténtelo de nuevo.');
            }
        });
    });
});

Ahora, si actualiza la página /planets, aparecerán los botones de "Dar de baja", y al hacer clic en ellos se activará nuestro código JavaScript!


4. Pasar datos de Blade a JavaScript

A veces es necesario pasar a JS no solo una cadena, sino un array o un objeto completo.

Forma incorrecta (vulnerable):

let planets = {{ $planets }}; // Esto causará un error de sintaxis y es inseguro

Forma correcta (a través de JSON): Utilice la directiva @json. Transforma de forma segura un array/objeto PHP en un objeto JSON válido.

Ejemplo en planets/index.blade.php:

@extends('layouts.app')
{{-- ... --}}
@section('content')
    {{-- ... --}}
@endsection

@push('scripts') {{-- "Empujamos" nuestro script al slot @stack('scripts') en la plantilla --}}
<script>
    // Laravel convierte de forma segura la colección $planets en un array JSON
    const planetsData = @json($planets);

    // Ahora podemos trabajar con este array en JS
    console.log('Datos de los planetas, pasados desde Blade:', planetsData);
    alert(`¡Se han cargado ${planetsData.length} planetas!`);
</script>
@endpush

  • @push('scripts') coloca el contenido dentro de @stack('scripts') en layouts/app.blade.php. Esto permite añadir scripts solo a las páginas donde realmente se necesitan.

Cuestionario de refuerzo

1. ¿Dónde deben almacenarse los archivos JS y CSS públicos en un proyecto Laravel?

2. ¿Qué ayudante de Blade se utiliza para generar correctamente la URL de los recursos (JS, CSS)?

3. ¿Qué hace el par de directivas `@push('scripts')` / `@stack('scripts')`?

4. ¿Cómo se pasa de forma segura un array PHP `$data` a JavaScript desde Blade?

5. ¿Por qué se recomienda incluir los scripts JS al final de la etiqueta body?


🚀 Resumen del capítulo:

Has aprendido a dar vida a las páginas estáticas de Blade, añadiendo lógica del lado del cliente. Habilidades clave:

  • Organización y conexión correctas de archivos JS en un proyecto Laravel.
  • Uso de atributos data-* para pasar datos de HTML a JS.
  • Interacción dinámica con la API usando Fetch sin recargar la página.
  • Paso seguro de variables PHP a JavaScript usando la directiva @json.
  • Organización de scripts usando @push y @stack.

Tus "paneles de control" se han vuelto interactivos. Sin embargo, nuestras solicitudes de modificación (POST, PUT, DELETE) son actualmente vulnerables. En el próximo capítulo añadiremos un mecanismo de protección crucial: los tokens CSRF.