Chapter 5.3: Embedding JavaScript in Laravel Views
Study time: 50 minutes
1. Two Approaches to JavaScript on the Web
So far, we have been working with Server-Side Rendering (SSR) — the server (Laravel) generated the finished HTML (via Blade) and sent it to the browser. This is great for SEO and fast initial loading.
Now we will add Client-Side Interactions — the browser, having already loaded the page, will execute JavaScript code to:
- Send requests to our API without reloading the page.
- Dynamically update parts of the page (e.g., adding a new planet to the list).
- Display notifications and modal windows.
💡 Space Analogy:
Imagine that SSR is receiving a complete map of a star system, printed at Mission Control (the server). You see all the objects at the time of printing.
Client-Side JS is your personal tablet (the browser) that communicates with satellites (the API) in real time and updates the position of objects on your map, without requesting a new paper map from Mission Control.
2. Where to Store and How to Include JS Code
As we have already found out, all public assets (CSS, JS, images) should be located in the public
folder.
The correct structure:
- Source files: All your main JS code should be in
public/js/
. For example,public/js/planets.js
. - Inclusion in Blade: Use the
asset()
helper to generate the correct URL.
Example of inclusion in the layouts/app.blade.php
layout:
<!DOCTYPE html>
<html>
<head>
{{-- ... --}}
</head>
<body>
{{-- ... header and main ... --}}
<footer>
<p>© {{ date('Y') }} Space Command.</p>
</footer>
{{-- It is better to include scripts at the end of the body to speed up page rendering --}}
<script src="{{ asset('js/planets.js') }}"></script>
@stack('scripts') {{-- Create a "slot" for scripts of a specific page --}}
</body>
</html>
@stack('scripts')
is a powerful Blade directive. It allows child views to "push" their own JS code into this place. This is useful when one page needs a unique script, and another does not.
3. Example: "Delete" Button with Confirmation
Let's add a "Delete" button for each planet on the planet list page (planets/index.blade.php
) that will work via JavaScript and the Fetch API.
Step 1: Add the button to resources/views/planets/index.blade.php
Modify the planet card by adding a button with data-attributes:
{{-- ... inside the @forelse loop ... --}}
<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="/planets/{{ $planet->id }}">Learn more →</a>
<button class="delete-btn" data-id="{{ $planet->id }}" data-url="/api/planets/{{ $planet->id }}">
Decommission
</button>
</div>
<!-- ... Before the closing body tag ... -->
<script src="{{ asset('js/planets.js') }}" defer></script>
id="planet-card-{{ $planet->id }}"
is a unique ID for the entire card so that we can remove it from the DOM.data-id
anddata-url
are a convenient way to pass data from PHP (Blade) to JavaScript.
Step 2: Write the JavaScript logic
Create a file public/js/planets.js
and add the following code to it:
document.addEventListener('DOMContentLoaded', () => {
// Find all "Delete" buttons
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(`Are you sure you want to decommission the planet with ID ${planetId}? This action is irreversible.`)) {
return; // The user clicked "Cancel"
}
try {
// Send a DELETE request to our API
const response = await fetch(apiUrl, {
method: 'DELETE',
headers: {
'Accept': 'application/json',
// We will add a CSRF token here later
}
});
if (response.status === 204) { // 204 No Content - successful deletion
// Remove the planet card from the page
const cardToRemove = document.getElementById(`planet-card-${planetId}`);
if (cardToRemove) {
cardToRemove.remove();
}
alert('Planet successfully decommissioned.');
} else {
// If the API returned an error
const errorData = await response.json();
alert(`Error: ${errorData.message || 'Failed to delete the planet.'}`);
}
} catch (error) {
console.error('Error sending the request:', error);
alert('A network error occurred. Please try again.');
}
});
});
});
Now, if you refresh the /planets
page, you will see "Decommission" buttons, and clicking them will trigger our JavaScript code!
4. Passing Data from Blade to JavaScript
Sometimes you need to pass not just a string to JS, but a whole array or object.
The wrong way (vulnerable):
The right way (via JSON):
Use the @json
directive. It safely converts a PHP array/object into a valid JSON object.
Example in planets/index.blade.php
:
@extends('layouts.app')
{{-- ... --}}
@section('content')
{{-- ... --}}
@endsection
@push('scripts') {{-- "Push" our script into the @stack('scripts') slot in the layout --}}
<script>
// Laravel safely converts the $planets collection into a JSON array
const planetsData = @json($planets);
// Now we can work with this array in JS
console.log('Planet data passed from Blade:', planetsData);
alert(`Loaded ${planetsData.length} planets!`);
</script>
@endpush
@push('scripts')
places the content inside@stack('scripts')
inlayouts/app.blade.php
. This allows you to add scripts only to the pages where they are really needed.
Quiz to Reinforce
🚀 Chapter Summary:
You have learned to breathe life into static Blade pages by adding client-side logic. Key skills:
- Proper organization and inclusion of JS files in a Laravel project.
- Using
data-*
attributes to pass data from HTML to JS. - Dynamic interaction with the API using Fetch without reloading the page.
- Safely passing PHP variables to JavaScript using the
@json
directive. - Organizing scripts using
@push
and@stack
.
Your "control panels" have become interactive. However, our modifying requests (POST, PUT, DELETE) are currently vulnerable. In the next chapter, we will add a crucial security mechanism — CSRF tokens.