在这篇文章中,我们将了解HTMX的工作原理,并在Laravel中使用HTMX构建一个非常基础的CRUD。
目录
什么是HTMX?
HTMX(HTML扩展)是一个易于使用的JavaScript库,它允许您直接在HTML中构建可反应的用户界面。
它通过引入超能力来扩展熟悉的HTML属性,允许您直接在标记中构建可反应的用户界面。
以下是整体工作流程:
- 用户在浏览器中触发事件(如点击或keyup)。
- 浏览器把此事件传递给HTMX。
- HTMX向服务器发出一个AJAX请求。
- 服务器响应HTML。
- HTMX使用新HTML更新DOM。
- 浏览器将更新后的页面显示给用户。
神奇的HTMX属性
HTMX通过引入一组新属性丰富了标准的HTML。这些属性赋予HTML元素发起HTTP请求、触发事件、指定目标和控制内容交换过程的能力。
一些关键的HTMX属性包括
-
hx-{get, post, put, delete}:这些属性定义请求的HTTP谓词,允许元素发出
GET
、POST
、PUT
和DELETE
请求。 -
hx-trigger:此属性定义启动请求的事件,例如基于
mouseover
或其他自定义交互。通常,HTMX会在按钮的clicks
或表单的submissions
等事件上自动触发请求。 -
hx-target:此属性使我们能够指定将响应内容放置的元素。
-
hx-swap:
hx-swap
属性确定如何替换目标元素的内容。有不同类型的替换策略,如innerHTML
、outerHTML
、delete
和更多。
设置 Laravel 玩乐场
在我们可以开始使用 HTMX 之前,我们需要一个可以玩转的 Laravel 应用。
Laravel 是一个构建网页应用的优秀 PHP 框架,让我们用它来启动一个基本的 CRUD 示例。
首先,我们将使用 Laravel 的 artisan 命令创建一个 联系人 模型和控制器。
php artisan make:model Contact --controller --migration
这为我们提供了开始所需的模型、迁移和控制器。接下来,我们在 routes/web.php
中为 ContactController 添加了一个资源路由。
Route::resource('contacts', ContactController::class);
由于 CRUD 应用程序的概念很标准,我不会过多地婆说婆说细节。
现在我们的 Laravel 玩乐场已经设置好了,是时候进入有趣的部分——集成 HTMX 了!
让我们 HTMX 化 Laravel 应用程序
首先,我们需要安装 HTMX 并使其在 Laravel 项目中可用。安装 HTMX 有几个选项,但我们将使用 CDN 方法快速开始。
让我们在布局 app.blade.php
中包含 CDN 链接
<script src="https://unpkg.com/[email protected]"
crossorigin="anonymous"></script>
既然我们已经将 HTMX 添加到工具箱中,让我们开始使用它!
搜索联系人类
我们的联系人类表页有一个标准的由服务器渲染的表格。功能齐全,但有点枯燥。让我们使用 HTMX 的客户端魔法来让它变得更有趣。
计划
- 将搜索输入连接到获取联系人类
- 仅针对表格体进行刷新
- 在控制器中检查 HTMX 请求
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
Contacts
</h2>
</x-slot>
<div id="content">
<div class="flex justify-between p-3">
<!-- Removed -->
<form action="{{ route('contacts.index') }}" class="flex gap-2">
<x-text-input name="q"
class="px-2 py-1 block" :value="request('q')"/>
<x-secondary-button type="submit" class="px-4 py-2">
Search
</x-secondary-button>
</form>
<!-- Added -->
<x-text-input name="q" class="px-2 py-1 block" :value="request('q')"
placeholder="Search contacts..."
hx-get="{{ route('contacts.index') }}"
hx-target="#contacts-table-body"
hx-trigger="keyup changed delay:500ms, search"/>
<x-primary-link href="{{ route('contacts.create') }}">
Create New Contact
</x-primary-link>
</div>
<table id="contacts-table" class="table-auto w-full">
<thead>
<!-- Table header here. -->
</thead>
<tbody id="contacts-table-body">
@include('contacts.partials.table-body')
</tbody>
</table>
</div>
</x-app-layout>
然后在控制器中检查请求类型
class ContactController extends Controller
{
public function index(Request $request)
{
$searchTerm = $request->input('q');
$contacts = Contact::where('name', 'LIKE', "%$searchTerm%")->get();
// Added
if ($request->header('hx-request')) {
return view('contacts.partials.table-body', compact('contacts'));
}
return view('contacts.index', compact('contacts'));
}
...
...
}
所以,仅仅通过几行代码,我们就已经实现了表格的响应式搜索
创建联系人类
让我们通过 HTMX 将创建新联系人类变得既顺滑又响应式。首先,在点击“新建联系人”时,我们将异步加载创建表单。
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
Contacts
</h2>
</x-slot>
<!-- Added -->
<div id="section">
{{-- Placeholder for the views --}}
</div>
<div id="content">
<div class="flex justify-between p-3">
<x-text-input name="q" class="px-2 py-1 block"
:value="request('q')" placeholder="Search contacts..."
hx-get="{{ route('contacts.index') }}"
hx-target="#contacts-table-body"
hx-trigger="keyup changed delay:500ms, search"/>
<!-- Removed -->
<x-primary-link href="{{ route('contacts.create') }}">
Create New Contact
</x-primary-link>
<!-- Added -->
<x-primary-link hx-get="{{ route('contacts.create') }}"
hx-target="#section">
Create New Contact
</x-primary-link>
</div>
<table id="contacts-table" class="table-auto w-full">
...
</table>
</div>
</x-app-layout>
然后我们将通过使用 hx-post 来提交表单,而无需重新加载。
<div id="partialCreate" class="p-5 border-b-8 border-b-gray-100">
<form hx-post="{{ route('contacts.store') }}"
hx-target="#partialCreate"
hx-swap="delete">
@csrf
@include('contacts.partials.form')
</form>
</div>
在成功后,我们希望刷新表格而无需重新加载页面。我们可以通过从服务器发送一个 HX-Trigger
标头来实现这一点,这将作为来自服务器端的发送给客户端以执行某些操作的事件。
class ContactController extends Controller
{
...
public function create()
{
return view('contacts.create');
}
public function store(ContactRequest $request)
{
$contact = Contact::create($request->all());
return response()->make($contact, 200, ['HX-Trigger' => 'loadContacts']);
}
...
...
}
回到 index.blade.php
,我们需要更新表格以监听来自服务器发送的 loadContacts
事件。这告诉 HTMX 向我们指定的路由发起请求以获取新的表格数据。我们添加 hx-get
和 hx-trigger
属性
<x-app-layout>
...
<div id="content">
...
<table id="contacts-table" class="table-auto w-full">
<thead>
<!-- Table header here. -->
</thead>
<tbody id="contacts-table-body"
hx-get="{{ route('contacts.index') }}"
hx-trigger="loadContacts from:body">
@include('contacts.partials.table-body')
</tbody>
</table>
</div>
</x-app-layout>
所以流程是这样的
- 服务器在创建联系人类时发送 "loadContacts" 事件
- hx-trigger 检测到事件
- 发起请求,加载最新的表格数据
- 替换新数据,而无需重新加载
查看/编辑/删除联系人类
现在我们已经看到了 HTMX 在搜索和创建中的使用,让我们快速实现 CRUD 的其余部分。
在 table-row.blade.php
中,我们可以添加用于平滑查看、编辑和删除的 HTMX 属性
<tr id="contact-{{ $contact->id }}">
<td class="px-4 py-2 border">{{ $contact->name }}</td>
<td class="px-4 py-2 border">{{ $contact->email }}</td>
<td class="px-4 py-2 border">{{ $contact->phone }}</td>
<td class="px-4 py-2 border">{{ $contact->address }}</td>
<td class="px-4 py-2 border">
<a class="mr-1 uppercase hover:underline"
hx-get="{{ route('contacts.show', $contact->id) }}"
hx-target="#section">View</a>
<a class="mr-1 uppercase hover:underline"
hx-get="{{ route('contacts.edit', $contact->id) }}"
hx-target="#section">Edit</a>
<a class="mr-1 uppercase hover:underline"
hx-delete="{{ route('contacts.destroy', $contact->id) }}"
hx-confirm="Are you sure you want to delete this contact?"
hx-headers='{"X-CSRF-TOKEN": "{{ csrf_token() }}"}'>Delete</a>
</td>
</tr>
在 edit.blade.php
中,我们使用 HTMX 来异步提交编辑表单
-
hx-put
属性在表单提交时发送 PUT 请求 -
hx-target="#partialEdit"
设置目标以便交换 -
hx-swap="delete"
指定了交换策略。例如,在这里,在接受到服务器响应后,它将delete
目标#partialEdit
。
<div id="partialEdit" class="p-5 border-b-8 border-b-gray-100">
<form hx-put="{{ route('contacts.update', $contact->id) }}"
hx-target="#partialEdit"
hx-swap="delete">
@csrf
@method('PUT')
@include('contacts.partials.form')
</form>
</div>
在 show.blade.php
中,我们添加 HTMX 来实现无全页刷新的导航
-
hx-get
用于异步获取页面 -
hx-target
用于更新内容区域 -
hx-swap
指定了交换策略。在这里,这意味着我们希望在服务器响应后删除目标#partialShow
。
<div id="partialShow" class="p-5 border-b-8 border-b-gray-100">
<h2 class="text-2xl font-bold">{{ $contact->name }}</h2>
<p class="text-gray-600">Email: {{ $contact->email }}</p>
<p class="text-gray-600">Phone: {{ $contact->phone }}</p>
<p class="text-gray-600">Address: {{ $contact->address }}</p>
<div class="flex items-center gap-2 mt-4">
<x-primary-button hx-get="{{ route('contacts.edit', $contact->id) }}"
hx-target="#section">Edit</x-primary-button>
<x-secondary-button hx-get="{{ route('contacts.index') }}"
hx-target="#partialShow"
hx-swap="delete">Go Back</x-secondary-button>
</div>
</div>
在结束之前,我想分享一下我们完整的 ContactController
。
class ContactController extends Controller
{
public function index(Request $request)
{
$searchTerm = $request->input('q');
$contacts = Contact::where('name', 'LIKE', "%$searchTerm%")->get();
if ($request->header('hx-request')) {
return view('contacts.partials.table-body', compact('contacts'));
}
return view('contacts.index', compact('contacts'));
}
public function create()
{
return view('contacts.create');
}
public function store(ContactRequest $request)
{
$contact = Contact::create($request->all());
return response()->make($contact, 200, ['HX-Trigger' => 'loadContacts']);
}
public function show(Contact $contact)
{
return view('contacts.show', compact('contact'));
}
public function edit(Contact $contact)
{
return view('contacts.edit', compact('contact'));
}
public function update(ContactRequest $request, Contact $contact)
{
$contact->update($request->all());
return response()->make($contact, 200, ['HX-Trigger' => 'loadContacts']);
}
public function destroy(Contact $contact)
{
$contact->delete();
return response()->make($contact, 200, ['HX-Trigger' => 'loadContacts']);
}
}
总结
这就是我们在 Laravel 中使用 HTMX 的介绍!正如我们所见,这只需要几行代码就能实现这种响应式功能。这有多酷啊?
虽然在这里我们介绍了基础知识,但我们可以使用HTMX在高级交换、动画、插件等方面做更多的事情。有关更多详细信息和技术案例,请查看HTMX文档。
请关注我的Twitter,以便在我想发布更多内容时得到通知。
driesvints, massimoselvi, mshaf 喜欢了这篇文章