Skip to content

Глава 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.

Правильная структура:

  1. Исходные файлы: Весь ваш основной JS-код должен лежать в public/js/. Например, public/js/planets.js.
  2. Подключение в Blade: Используйте хелпер asset() для генерации правильного URL.

Пример подключения в макете layouts/app.blade.php:

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

    <footer>
        <p>&copy; {{ 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 }}">Узнать больше &rarr;</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 не просто строку, а целый массив или объект.

Неправильный способ (уязвимый):

let planets = {{ $planets }}; // Это приведет к ошибке синтаксиса и небезопасно

Правильный способ (через 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. Это позволяет добавлять скрипты только на те страницы, где они действительно нужны.

Квиз для закрепления

1. Где в Laravel-проекте должны храниться публичные JS и CSS файлы?

2. Какой Blade-хелпер используется для правильной генерации URL к ассетам (JS, CSS)?

3. Что делает пара директив `@push('scripts')` / `@stack('scripts')`?

4. Как безопасно передать PHP-массив `$data` в JavaScript из Blade?

5. Почему JS-скрипты рекомендуется подключать в конце тега body?


🚀 Итог главы:

Вы научились вдыхать жизнь в статичные Blade-страницы, добавляя клиентскую логику. Ключевые навыки:

  • Правильная организация и подключение JS-файлов в Laravel-проекте.
  • Использование data-* атрибутов для передачи данных из HTML в JS.
  • Динамическое взаимодействие с API с помощью Fetch без перезагрузки страницы.
  • Безопасная передача PHP-переменных в JavaScript с помощью директивы @json.
  • Организация скриптов с помощью @push и @stack.

Ваши "панели управления" стали интерактивными. Однако сейчас наши изменяющие запросы (POST, PUT, DELETE) уязвимы. В следующей главе мы добавим важнейший механизм защиты — CSRF-токены.