Глава 5.3: Встраивание JavaScript в Laravel Views
Время изучения: 50 минут
1. Два подхода к JavaScript в вебе
До сих пор мы работали с Server-Side Rendering (SSR) — сервер (Laravel) генерировал готовый HTML (через Blade) и отправлял его в браузер. Это отлично для SEO и быстрой первой загрузки.
Теперь мы добавим Client-Side Interactions — браузер, уже загрузив страницу, будет выполнять JavaScript-код для:
- Отправки запросов на наш API без перезагрузки страницы.
- Динамического обновления частей страницы (например, добавления новой планеты в список).
- Отображения уведомлений и модальных окон.
💡 Космическая аналогия:
Представьте, что SSR — это получение полной карты звездной системы, распечатанной в ЦУП (сервер). Вы видите все объекты на момент печати.
Client-Side JS — это ваш личный планшет (браузер), который в реальном времени связывается со спутниками (API) и обновляет положение объектов на вашей карте, не запрашивая новую бумажную карту из ЦУП.
2. Где хранить и как подключать JS-код
Как мы уже выяснили, все публичные ассеты (CSS, JS, изображения) должны находиться в папке public
.
Правильная структура:
- Исходные файлы: Весь ваш основной JS-код должен лежать в
public/js/
. Например,public/js/planets.js
. - Подключение в Blade: Используйте хелпер
asset()
для генерации правильного URL.
Пример подключения в макете layouts/app.blade.php
:
<!DOCTYPE html>
<html>
<head>
{{-- ... --}}
</head>
<body>
{{-- ... header и main ... --}}
<footer>
<p>© {{ date('Y') }} Space Command.</p>
</footer>
{{-- Скрипты лучше подключать в конце body для ускорения рендеринга страницы --}}
<script src="{{ asset('js/planets.js') }}"></script>
@stack('scripts') {{-- Создаем "слот" для скриптов конкретной страницы --}}
</body>
</html>
@stack('scripts')
— это мощная директива Blade. Она позволяет дочерним видам "проталкивать" свой собственный JS-код в это место. Это полезно, когда для одной страницы нужен уникальный скрипт, а для другой — нет.
3. Пример: Кнопка "Удалить" с подтверждением
Давайте добавим на страницу списка планет (planets/index.blade.php
) кнопку "Удалить" для каждой планеты, которая будет работать через JavaScript и Fetch API.
Шаг 1: Добавляем кнопку в resources/views/planets/index.blade.php
Измените карточку планеты, добавив кнопку с data-атрибутами:
{{-- ... внутри цикла @forelse ... --}}
<div class="planet-card" id="planet-card-{{ $planet->id }}">
<h3>{{ $planet->name }}</h3>
<p>Солнечная система: {{ $planet->solar_system }}</p>
<p>Диаметр: {{ number_format($planet->size_km, 0, '.', ' ') }} км</p>
<a href="/planets/{{ $planet->id }}">Узнать больше →</a>
<button class="delete-btn" data-id="{{ $planet->id }}" data-url="/api/planets/{{ $planet->id }}">
Списать
</button>
</div>
<!-- ... Перед закрывающейся тегом body ... -->
<script src="{{ asset('js/planets.js') }}" defer></script>
id="planet-card-{{ $planet->id }}"
— уникальный ID для всей карточки, чтобы мы могли ее удалить из DOM.data-id
иdata-url
— удобный способ передать данные из PHP (Blade) в JavaScript.
Шаг 2: Пишем JavaScript-логику
Создайте файл public/js/planets.js
и добавьте в него следующий код:
document.addEventListener('DOMContentLoaded', () => {
// Находим все кнопки "Удалить"
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(`Вы уверены, что хотите списать планету с ID ${planetId}? Это действие необратимо.`)) {
return; // Пользователь нажал "Отмена"
}
try {
// Отправляем DELETE-запрос на наш API
const response = await fetch(apiUrl, {
method: 'DELETE',
headers: {
'Accept': 'application/json',
// Позже мы добавим сюда CSRF-токен
}
});
if (response.status === 204) { // 204 No Content - успешное удаление
// Удаляем карточку планеты со страницы
const cardToRemove = document.getElementById(`planet-card-${planetId}`);
if (cardToRemove) {
cardToRemove.remove();
}
alert('Планета успешно списана.');
} else {
// Если API вернул ошибку
const errorData = await response.json();
alert(`Ошибка: ${errorData.message || 'Не удалось удалить планету.'}`);
}
} catch (error) {
console.error('Ошибка при отправке запроса:', error);
alert('Произошла сетевая ошибка. Попробуйте снова.');
}
});
});
});
Теперь, если вы обновите страницу /planets
, у вас появятся кнопки "Декомиссия", и при нажатии на них будет срабатывать наш JavaScript-код!
4. Передача данных из Blade в JavaScript
Иногда нужно передать в JS не просто строку, а целый массив или объект.
Неправильный способ (уязвимый):
Правильный способ (через JSON):
Используйте директиву @json
. Она безопасно преобразует PHP-массив/объект в валидный JSON-объект.
Пример в planets/index.blade.php
:
@extends('layouts.app')
{{-- ... --}}
@section('content')
{{-- ... --}}
@endsection
@push('scripts') {{-- "Проталкиваем" наш скрипт в слот @stack('scripts') в макете --}}
<script>
// Laravel безопасно преобразует коллекцию $planets в JSON-массив
const planetsData = @json($planets);
// Теперь мы можем работать с этим массивом в JS
console.log('Данные о планетах, переданные из Blade:', planetsData);
alert(`Загружено ${planetsData.length} планет!`);
</script>
@endpush
@push('scripts')
помещает содержимое внутрь@stack('scripts')
вlayouts/app.blade.php
. Это позволяет добавлять скрипты только на те страницы, где они действительно нужны.
Квиз для закрепления
🚀 Итог главы:
Вы научились вдыхать жизнь в статичные Blade-страницы, добавляя клиентскую логику. Ключевые навыки:
- Правильная организация и подключение JS-файлов в Laravel-проекте.
- Использование
data-*
атрибутов для передачи данных из HTML в JS. - Динамическое взаимодействие с API с помощью Fetch без перезагрузки страницы.
- Безопасная передача PHP-переменных в JavaScript с помощью директивы
@json
. - Организация скриптов с помощью
@push
и@stack
.
Ваши "панели управления" стали интерактивными. Однако сейчас наши изменяющие запросы (POST, PUT, DELETE) уязвимы. В следующей главе мы добавим важнейший механизм защиты — CSRF-токены.