Работа с Ajax в Alpine.js
29-11-2020Время чтения ~ 4 мин.Alpine.js 8941
Я рассматриваю 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 **/ // отсекаем всё, что без заголовка 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, желающие могут сравнить какой код проще и удобней. ;-)
Хорошая статья. С ajax подгрузкой списка через x-for понятно. Но это происходит по событиям мыши. А что alpine предлагает для первичного рендеринга страницы? Возможно ли подгружать список сразу? Или сначала идет рендер страницы, потом снова запускается ajax и генерируется список. Не получается ли тогда как-бы двойной запрос, и alpine становится лишним звеном? Если это работает как-то по-другому, можешь показать?
Если нужно получить данные сразу, то можно использовать директиву x-init. Тогда данные загрузятся в момент инициализации компонента.
Выходит двойной запрос, и есть задержка перед отрисовкой: пока alpine инициализируется, пока выполнит запрос. Да и если использовать на фронте, не понятно как поисковики все это будут индексировать. Альпайн хорош, но чёт как-то смущают эти вещи.
Alpine работает без задержек. Лучше её в HEAD подключать, тогда даже не будет притормаживания на её загрузку. Двойного запроса никакого нет.
Просто аякс делает запрос на другой сервер и это создает задержку. Можно fetch использовать асинхронно. Это есть в документации.
Индексация поисковиком обычная, как и любого другого js-кода. Понятно, что основной контент не стоит добавлять через js. По-моему это очевидные вещи.