Работа с Ajax в Alpine.js

Я рассматриваю Alpine.js в первую очередь, как альтернативу jQuery, поэтому нужно разобраться как в Alpine выполнять то, что давно уже привычно в jQuery. Отправка AJAX как раз один из таких моментов.

Хочу показать несколько примеров AJAX, правда вместо привычного XMLHttpRequest(), я буду использовать fetch(). Эта возможность уже года 3 как доступна во всех браузерах. Сам по себе fetch()несложен, хотя есть разные нюансы. Я к тому, если вы не понимаете что это такое, то погуглите, информации полно.

Итак, для начала сделаем простой пример получения json-данных и выводе их на сайте.

Получение и вывод JSON-данных

В качестве источника JSON-данных будем использовать сайт jsonplaceholder.typicode.com с данными users.

<div x-data="{users: []}">
    <button class="button button1" @click="
        fetch('https://jsonplaceholder.typicode.com/users/1')
        .then(response => response.json())
        .then(response => users = response)
        ">Получить имя</button>
  
    <div><span x-text="users.name"></span> <span x-text="users.email"></span></div>
</div>

Директива x-dataуказывает на то, что это компонент Alpine. Мы сразу инициализируем переменную users, которая будет массивом/объектом полученных данных.

Если перейти по указанному адресу, то увидим обычные JSON-данные одного пользователя. Поэтому отображение результата будет происходить в SPAN с директивами x-text. В одном тэге это имя юзера, в другом его email.

Получение данных «вешаем» на click кнопки: прописываем получение данных через fetch(). Здесь происходит get-запрос по указанному URL и данные в формате JSON присваиваются нашей переменной users. В целом это достаточно типовой вариант работы с fetch().

Если данные представляют собой многомерный массив, то для его вывода нужно использовать директиву x-for.

<div x-data="{users: []}">
    <button class="button button1" @click="
        fetch('https://jsonplaceholder.typicode.com/users')
        .then(response => response.json())
        .then(response => users = response)
        ">Получить пользователей</button>
  
    <template x-for="user in users" :key="user.id">
        <div class="b-flex">
            <div x-text="user.id + '.'" class="w5"></div>
            <div x-text="user.name" class="w20"></div>
            <div x-text="user.email"></div>
        </div>
    </template>
</div>

Тут всё практически тоже самое, только по новому адресу получается не один элемент, а множество. Директива для вывода в цикле x-forв Alpine применяется только к тэгу TEMPLATE (как принято с web-компонентами).

Отправка произвольных данных по AJAX

Теперь рассмотрим вариант, если нужно сформировать произвольный Ajax-запрос без создания html-формы. Отправлять post-запрос мы будем уже на свой адрес, а ответ получать в виде текста/html.

Обработчик ajax-запроса представляет собой очень простую страницу. Если вы используете Albireo, то можете сделать такой файл:

<?php if (!defined('BASE_DIR')) exit('No direct script access allowed');
/**
slug: sample1
method: AJAX
layout: empty.php
slug-static: -
**/
 
// отсекаем всё, что без заголовка AJAX
if (!isset($_SERVER['HTTP_X_REQUESTED_WITH'])) exit('Error: AJAX only!');
 
echo '<b>Привет!</b>';
 
pr($_POST); // вывод входящего POST

Адрес (slug) можно указать другой. Он указывается в fetch-запросе.

Теперь код примера:

<div x-data="{textData: ''}">
    <button class="button button1" @click="
        var form = new FormData();
        
        form.append('_method', 'AJAX');
        form.append('user', '1');
        
        fetch('<?= SITE_URL ?>sample1', {
            method: 'POST',
            headers: {
                'X-Requested-With': 'XMLHttpRequest',
            }, 
            body: form
        })
        .then(response => response.text())
        .then(response => textData = response)
        ">Проверить AJAX</button>
  
    <div x-html="textData"></div>
</div>

Первое отличие — адрес fetch-запроса. Второе — дополнительные параметры. В данном случае это method, headersи body. Заголовок мы отправляем для того, чтобы в php-обработчике определить, что это именно Ajax-запрос.

Если нужен обычный POST, то этот заголовок можно не указывать и не отслеживать в обработчике.

Тело запроса формируем его из нового объекта FormData. Сейчас в основном именно так и делают вместо обработки данных. Поле _methodнужно для роутинга Albireo (описание и примеры есть в документации). То есть данные для отправки формируются динамически. При желании можно привязать те же поля ввода.

Поскольку ответ — это обычный текст с разметкой HTML, то используем директиву x-html. Если же использовать x-text, то ответ мы увидим в виде обычного текста.

Отправка формы через Ajax

Обычную форму можно отправить похожим образом.

<div x-data="{textData: ''}">
    <form x-ref="form" @submit="
        fetch('<?= SITE_URL ?>sample1', {
            method: 'POST',
            headers: {
                'X-Requested-With': 'XMLHttpRequest',
            },
            body: new FormData($refs.form)
        })
        .then(response => response.text())
        .then(response => textData = response);
        
        return false;
        ">
        <input type="hidden" name="_method" value="AJAX">
        <input class="form-input" type="text" name="name">
        <button class="button button1" type="submit">Отправить</button>
    </form>
 
    <div x-html="textData"></div>
</div>

Это самая обычная форма, где мы ловим событие submit. Обратите внимание, что функция возвращает false— это нужно для того, чтобы блокировать обычную отправку данных.

Данные мы формируем через FormData, как и в прошлый раз, только в качестве источника указываем текущую форму. Это $refs.form.

Переменная $refsв Alpine особая («магическая»), которая хранит DOM-элементы. В данном случае это form, который мы указали у самой формы в виде директивы x-ref.

С помощью x-refи $refsв Alpine реализуется получение любого элемента. Если бы мы делали этот пример на нативном JavaScript, то использовали бы getElementById()или querySelector(), чтобы получить доступ к данным формы.

Итого

Для Ajax пришлось написать js-код, который достаточно простой и однозначно не сложнее jQuery. Поскольку используются собственные директивы Alpine, то код получается меньше и проще для понимания. В последнем примере нам даже не пришлось задумываться как передать данные формы - x-ref— намного проще, чем querySelector(). Пару лет назад я писал про Ajax для jQuery, желающие могут сравнить какой код проще и удобней. ;-)

Комментариев: 4 RSS

1Ян29-11-2020 12:25

Хорошая статья. С ajax подгрузкой списка через x-for понятно. Но это происходит по событиям мыши. А что alpine предлагает для первичного рендеринга страницы? Возможно ли подгружать список сразу? Или сначала идет рендер страницы, потом снова запускается ajax и генерируется список. Не получается ли тогда как-бы двойной запрос, и alpine становится лишним звеном? Если это работает как-то по-другому, можешь показать?

2MAX29-11-2020 12:32

Если нужно получить данные сразу, то можно использовать директиву x-init. Тогда данные загрузятся в момент инициализации компонента.

3Ян29-11-2020 12:40

Выходит двойной запрос, и есть задержка перед отрисовкой: пока alpine инициализируется, пока выполнит запрос. Да и если использовать на фронте, не понятно как поисковики все это будут индексировать. Альпайн хорош, но чёт как-то смущают эти вещи.

4MAX29-11-2020 12:47

Alpine работает без задержек. Лучше её в HEAD подключать, тогда даже не будет притормаживания на её загрузку. Двойного запроса никакого нет.

Просто аякс делает запрос на другой сервер и это создает задержку. Можно fetch использовать асинхронно. Это есть в документации.

Индексация поисковиком обычная, как и любого другого js-кода. Понятно, что основной контент не стоит добавлять через js. По-моему это очевидные вещи.

Оставьте комментарий!

Комментарий будет опубликован после проверки. Вы соглашаетесь с правилами сайта.

(обязательно)