人们在首次使用 htmx 时经常会问一个问题,
“我需要更新屏幕上的其他内容。我该怎么做呢?”
有多种方法可以做到这一点,在本例中,我们将引导您完成其中一些方法。
我们将使用以下基本 UI 来讨论这个概念:一个简单的联系人表格,以及下方用于使用 hx-post 向表格添加新联系人的表单。
<h2>Contacts</h2>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody id="contacts-table">
...
</tbody>
</table>
<h2>Add A Contact</h2>
<form hx-post="/contacts">
<label>
Name
<input name="name" type="text">
</label>
<label>
Email
<input name="email" type="email">
</label>
</form>
这里的问题是,当您在表单中提交新的联系人时,您希望上面的联系人表格刷新并包含表单刚添加的联系人。
我们有哪些解决方案呢?
这里最简单的解决方案是将表单的“目标”扩展到包含表格和表单。例如,您可以将整个内容包裹在一个 div
中,然后在表单中定位该 div
。
<div id="table-and-form">
<h2>Contacts</h2>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody id="contacts-table">
...
</tbody>
</table>
<h2>Add A Contact</h2>
<form hx-post="/contacts" hx-target="#table-and-form">
<label>
Name
<input name="name" type="text">
</label>
<label>
Email
<input name="email" type="email">
</label>
</form>
</div>
请注意,我们使用 hx-target 属性定位封闭的 div。您需要在对 /contacts
的 POST
的响应中渲染表格和表单。
这是一种简单可靠的方法,尽管它可能并不显得特别优雅。
解决这个问题的另一种更复杂的方法是使用 带外交换 将更新后的内容交换到 DOM 中。
使用这种方法,HTML 从最初的设置开始就无需更改。
<h2>Contacts</h2>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody id="contacts-table">
...
</tbody>
</table>
<h2>Add A Contact</h2>
<form hx-post="/contacts">
<label>
Name
<input name="name" type="text">
</label>
<label>
Email
<input name="email" type="email">
</label>
</form>
您无需在前端修改任何东西,只需在对 /contacts
的 POST
的响应中包含一些额外内容。
<tbody hx-swap-oob="beforeend:#contacts-table">
<tr>
<td>Joe Smith</td>
<td>joe@smith.com</td>
</tr>
</tbody>
<label>
Name
<input name="name" type="text">
</label>
<label>
Email
<input name="email" type="email">
</label>
此内容使用 hx-swap-oob 属性将自身附加到 #contacts-table
,在成功添加联系人后更新表格。
一种更复杂的方法是,在成功创建联系人时触发客户端事件,然后在表格上监听该事件,从而使表格刷新。
<h2>Contacts</h2>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody id="contacts-table" hx-get="/contacts/table" hx-trigger="newContact from:body">
...
</tbody>
</table>
<h2>Add A Contact</h2>
<form hx-post="/contacts">
<label>
Name
<input name="name" type="text">
</label>
<label>
Email
<input name="email" type="email">
</label>
</form>
我们添加了一个新的端点 /contacts/table
,它重新渲染联系人表格。我们触发此请求的事件是自定义事件 newContact
。我们在 body
上监听此事件,因为当表单响应触发它时,它最终会由于事件冒泡而命中 body
。
当对 /contacts
的 POST 成功创建联系人时,响应将包含一个 HX-Trigger 响应头,如下所示
HX-Trigger:newContact
这将触发表格发出对 /contacts/table
的 GET
请求,并渲染新添加的联系人行
(以及表格的其余部分)。
非常干净,事件驱动的编程!
最后一种方法是使用 RESTful 路径依赖来刷新表格。Intercooler.js 是 htmx 的前身,它将 基于路径的依赖 集成到库中。
htmx 放弃了这一核心功能,但支持一个扩展,路径依赖,它为您提供了类似的功能。
将我们的示例更新为使用该扩展将涉及加载扩展的 JavaScript 代码,然后像这样注释我们的 HTML 代码
<h2>Contacts</h2>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody id="contacts-table" hx-get="/contacts/table" hx-ext="path-deps" hx-trigger="path-deps" path-deps="/contacts">
...
</tbody>
</table>
<h2>Add A Contact</h2>
<form hx-post="/contacts">
<label>
Name
<input name="name" type="text">
</label>
<label>
Email
<input name="email" type="email">
</label>
</form>
现在,当表单发布到 /contacts
URL 时,path-deps
扩展将检测到这一点,并在联系人表格上触发 path-deps
事件,从而触发请求。
这里的优点是,您无需使用响应头进行任何特殊操作。缺点是,即使联系人未成功创建,也会在每次 POST
请求时发出请求。
通常情况下,我建议使用第一种方法,扩展您的目标,尤其是当需要更新的元素在 DOM 中彼此比较接近时。它简单可靠。
在此之后,我认为自定义事件和带外交换方法旗鼓相当。我更倾向于自定义事件方法,因为我喜欢面向事件的系统,但这只是个人偏好。您选择哪一种应该由您自己的软件工程品味决定,以及哪一种更符合您选择的服务器端技术。
最后,路径依赖方法很有趣,如果它与您的思维模型和整体系统架构相匹配,它可以成为避免显式刷新的有趣方法。不过,除非这个概念真正吸引您,否则我会把它放在最后考虑。