编程

Laravel Htmx

333 2023-11-30 22:54:00

Laravel htmx 是一个来自 Maurizio Bonani 的包,它提供了一种使用 htmx 的好方法,该库允许您直接从 HTML 访问现代浏览器功能,而不是使用 JavaScript

htmx gives you access to AJAX, CSS Transitions, WebSockets and Server Sent Events directly in HTML, using attributes, so you can build modern user interfaces with the simplicity and power of hypertext

htmx is small (~14k min.gz’d), dependency-free, extendable, IE11 compatible & has reduced code base sizes by 67% when compared with react

Htmx 请求

你可以从容器中解析 HtmxRequest 实例,它提供了读取 htmx 指定请求头的快捷方式。

use Mauricius\LaravelHtmx\Http\HtmxRequest;
 
Route::get('/', function (HtmxRequest $request)
{
    // always true if the request is performed by Htmx
    $request->isHtmxRequest();
    // indicates that the request is via an element using hx-boost
    $request->isBoosted();
    // the current URL of the browser
    $request->getCurrentUrl();
    // true if the request is for history restoration after a miss in the local history cache
    $request->isHistoryRestoreRequest()
    // the user response to an hx-prompt
    $request->getPromptResponse();
    //     the id of the target element if it exists
    $request->getTarget();
    // the name of the triggered element if it exists
    $request->getTriggerName();
    // the id of the triggered element if it exists
    $request->getTriggerId();
});

Htmx 响应

  • HtmxResponseClientRedirect

当收到 HX-Redirect 响应头时,htmx 可以触发客户端重定向,HtmxResponseClientRedirect 使得触发这样的重定向变得容易。

use Mauricius\LaravelHtmx\Http\HtmxResponseClientRedirect;
 
Route::get('/', function (HtmxRequest $request)
{
    return new HtmxResponseClientRedirect('/somewhere-else');
});
  • HtmxResponseClientRefresh

如果收到 HX-Refresh 响应头,htmx 会触发页面重载。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) // Allows you to do a client-side redirect that does not do a full page reload
        ->pushUrl($url) // pushes a new url into the history stack
        ->replaceUrl($url) // replaces the current URL in the location bar
        ->reswap($option) // Allows you to specify how the response will be swapped
        ->retarget($selector); // A CSS selector that updates the target of the content update to a different element on the page
});

此外,你可以使用 addTrigger 方法触发客户端事件。

use Mauricius\LaravelHtmx\Http\HtmxResponse;
 
Route::get('/', function (HtmxRequest $request)
{
    return with(new HtmxResponse())
        ->addTrigger($event)
        ->addTriggerAfterSettle($event)
        ->addTriggerAfterSwap($event);
});

你可以多次调用这些方法,触发多个事件。

使用 Htmx 渲染 Blade 片段

这个库还提供了一个基本的 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>

模板中定义了 fragment 后,我们现在可以渲染整个模板:

Route::get('/', function ($id) {
    $contact = Contact::find($id);
 
    return View::make('contacts.detail', compact('contact'));
});

或者使用 \Illuminate\View\View 类定义的 renderFragment macro,只渲染模板的 archive-ui fragment 片段:

Route::patch('/contacts/{id}/unarchive', function ($id) {
    $contact = Contact::find($id);
 
    // The following approaches are equivalent
 
    // Using the View Facade
    return \Illuminate\Support\Facades\View::renderFragment('contacts.detail', 'archive-ui', compact('contact'));
 
    // Using the view() helper
    return view()->renderFragment('contacts.detail', 'archive-ui', compact('contact'));
 
    // Using the HtmxResponse Facade
    return \Mauricius\LaravelHtmx\Facades\HtmxResponse::renderFragment('contacts.detail', 'archive-ui', compact('contact'));
 
    // Using the HtmxResponse class
    return with(new \Mauricius\LaravelHtmx\Http\HtmxResponse())
        ->renderFragment('contacts.detail', 'archive-ui', compact('contact'));
});

OOB 交换支持

htmx 支持通过返回多个带有 hx-swap-oop 的多个响应更新多个目标。使用该库你可以使用 HtmxResponse 作为返回类型,返回多个片段。

比如,假设我们希望使用对 /todos/{id}的 PATCH 请求将 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'));
});

更多详情查看 GitHub.