Пакет Laravel htmx
htmx предоставляет доступ к AJAX, CSS Transitions, WebSockets и Server Sent Events непосредственно в HTML, используя атрибуты, что позволяет создавать современные пользовательские интерфейсы с простотой и мощью гипертекста.
htmx имеет небольшой размер (~14k min.gz), не содержит зависимостей, расширяем, совместим с IE11 и сократил размер кодовой базы на 67% по сравнению с react
Рассмотрим, какие возможности пакета laravel-htmx описаны в его readme:
Htmx Request
Из контейнера можно разрешить экземпляр HtmxRequest
, предоставляющий ярлыки для чтения заголовков запросов, специфичных для htmx.
use Mauricius\LaravelHtmx\Http\HtmxRequest;
Route::get('/', function (HtmxRequest $request)
{
// всегда true, если запрос выполняется с помощью Htmx
$request->isHtmxRequest();
// указывает на то, что запрос осуществляется через элемент, использующий hx-boost
$request->isBoosted();
// текущий URL-адрес браузера
$request->getCurrentUrl();
// true, если запрос направлен на восстановление истории после пропущенного события в локальном кэше истории
$request->isHistoryRestoreRequest()
// ответ пользователя на hx-prompt
$request->getPromptResponse();
// id целевого элемента, если он существует
$request->getTarget();
// имя сработавшего элемента, если он существует
$request->getTriggerName();
// id сработавшего элемента, если он существует
$request->getTriggerId();
});
Htmx Response
HtmxResponseClientRedirect
htmx может вызывать перенаправление на стороне клиента, когда получает ответ с заголовком HX-Redirect
. HtmxResponseClientRedirect
позволяет легко инициировать такое перенаправление.
use Mauricius\LaravelHtmx\Http\HtmxResponseClientRedirect;
Route::get('/', function (HtmxRequest $request)
{
return new HtmxResponseClientRedirect('/somewhere-else');
});
HtmxResponseClientRefresh
htmx запускает перезагрузку страницы, когда получает ответ с заголовком HX-Refresh
. HtmxResponseClientRefresh
— это пользовательский класс ответа, позволяющий отправить такой ответ. Он не принимает никаких аргументов, поскольку htmx игнорирует любое содержимое.
use Mauricius\LaravelHtmx\Http\HtmxResponseClientRefresh;
Route::get('/', function (HtmxRequest $request)
{
return new HtmxResponseClientRefresh();
});
HtmxResponseStopPolling
При использовании триггера опроса htmx прекращает опрос, когда встречает ответ со специальным кодом состояния HTTP 286
. HtmxResponseStopPolling
— это пользовательский класс ответа с таким кодом состояния.
use Mauricius\LaravelHtmx\Http\HtmxResponseStopPolling;
Route::get('/', function (HtmxRequest $request)
{
return new HtmxResponseStopPolling();
});
Для всех остальных доступных заголовков можно использовать класс HtmxResponse
.
use Mauricius\LaravelHtmx\Http\HtmxResponse;
Route::get('/', function (HtmxRequest $request)
{
return with(new HtmxResponse())
->location($location) // Позволяет выполнять переадресацию на стороне клиента без полной перезагрузки страницы
->pushUrl($url) // вставляет новый url в стек истории
->replaceUrl($url) // заменяет текущий URL в строке адресации
->reswap($option) // Позволяет указать, как будет происходить замена ответа
->retarget($selector); // CSS-селектор, который изменяет цель обновления содержимого на другой элемент на странице
});
Кроме того, можно запускать события на стороне клиента с помощью метода addTrigger
.
use Mauricius\LaravelHtmx\Http\HtmxResponse;
Route::get('/', function (HtmxRequest $request)
{
return with(new HtmxResponse())
->addTrigger($event)
->addTriggerAfterSettle($event)
->addTriggerAfterSwap($event);
});
Вы можете вызывать эти методы несколько раз, чтобы вызвать несколько событий.
Рендеринг Blade Fragments с помощью Htmx
Эта библиотека также предоставляет базовое Blade расширение для отрисовки фрагментов шаблонов.
Библиотека предоставляет две новые директивы Blade: @fragment
и @endfragment
. С помощью этих директив можно указать блок содержимого в шаблоне и вывести только этот блок. Например:
{{-- /contacts/detail.blade.php --}}
<html>
<body>
<div hx-target="this">
@fragment("archive-ui")
@if($contact->archived)
<button hx-patch="/contacts/{{ $contact->id }}/unarchive">Unarchive</button>
@else
<button hx-delete="/contacts/{{ $contact->id }}">Archive</button>
@endif
@endfragment
</div>
<h3>Contact</h3>
<p>{{ $contact->email }}</p>
</body>
</html>
Определив этот фрагмент в шаблоне, мы можем отобразить его целиком:
Route::get('/', function ($id) {
$contact = Contact::find($id);
return View::make('contacts.detail', compact('contact'));
});
Или же мы можем отобразить только фрагмент шаблона archive-ui
, используя макрос renderFragment
, определённый в классе \Illuminate\View\View
:
Route::patch('/contacts/{id}/unarchive', function ($id) {
$contact = Contact::find($id);
// Следующие подходы являются эквивалентными
// Использование Фасада View
return \Illuminate\Support\Facades\View::renderFragment('contacts.detail', 'archive-ui', compact('contact'));
// Использование хелпера view()
return view()->renderFragment('contacts.detail', 'archive-ui', compact('contact'));
// Использование Фасада HtmxResponse
return \Mauricius\LaravelHtmx\Facades\HtmxResponse::renderFragment('contacts.detail', 'archive-ui', compact('contact'));
// Использование класса HtmxResponse
return with(new \Mauricius\LaravelHtmx\Http\HtmxResponse())
->renderFragment('contacts.detail', 'archive-ui', compact('contact'));
});
Поддержка OOB Swap
htmx поддерживает обновление нескольких целей путём возврата нескольких частичных ответов с помощью hx-swap-oob
. С помощью этой библиотеки вы можете возвращать несколько фрагментов, используя HtmxResponse
в качестве возвращаемого типа.
Допустим, мы хотим пометить todo как завершённое с помощью PATCH-запроса к /todos/{id}
. Этим же запросом мы хотим обновить в нижнем колонтитуле количество оставшихся todo:
{{-- /todos.blade.php --}}
<html>
<body>
<main hx-target="this">
<section>
<ul class="todo-list">
@fragment("todo")
<li id="todo-{{ $todo->id }}" @class(['completed' => $todo->done])>
<input
type="checkbox"
class="toggle"
hx-patch="/todos/{{ $todo->id }}"
@checked($todo->done)
hx-target="#todo-{{ $todo->id }}"
hx-swap="outerHTML"
/>
{{ $todo->name }}
</li>
@endfragment
</ul>
</section>
<footer>
@fragment("todo-count")
<span id="todo-count" hx-swap-oob="true">
<strong>{{ $left }} items left</strong>
</span>
@endfragment
</footer>
</main>
</body>
</html>
Мы можем использовать HtmxResponse
для возвращения нескольких фрагментов:
Route::patch('/todos/{id}', function ($id) {
$todo = Todo::find($id);
$todo->done = !$todo->done;
$todo->save();
$left = Todo::where('done', 0)->count();
return HtmxResponse::addFragment('todomvc', 'todo', compact('todo'))
->addFragment('todomvc', 'todo-count', compact('left'));
});
Подробнее о пакете laravel-htmx можно узнать на GitHub.