更新其他内容

人们在首次使用 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>

这里的问题是,当您在表单中提交新的联系人时,您希望上面的联系人表格刷新并包含表单刚添加的联系人。

我们有哪些解决方案呢?

#解决方案 1:扩展目标

这里最简单的解决方案是将表单的“目标”扩展到包含表格表单。例如,您可以将整个内容包裹在一个 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。您需要在对 /contactsPOST 的响应中渲染表格和表单。

这是一种简单可靠的方法,尽管它可能并不显得特别优雅。

#解决方案 2:带外响应

解决这个问题的另一种更复杂的方法是使用 带外交换 将更新后的内容交换到 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>

您无需在前端修改任何东西,只需在对 /contactsPOST 的响应中包含一些额外内容。

<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,在成功添加联系人后更新表格。

#解决方案 3:触发事件

一种更复杂的方法是,在成功创建联系人时触发客户端事件,然后在表格上监听该事件,从而使表格刷新。

<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/tableGET 请求,并渲染新添加的联系人行
(以及表格的其余部分)。

非常干净,事件驱动的编程!

#解决方案 4:使用路径依赖扩展

最后一种方法是使用 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 中彼此比较接近时。它简单可靠。

在此之后,我认为自定义事件和带外交换方法旗鼓相当。我更倾向于自定义事件方法,因为我喜欢面向事件的系统,但这只是个人偏好。您选择哪一种应该由您自己的软件工程品味决定,以及哪一种更符合您选择的服务器端技术。

最后,路径依赖方法很有趣,如果它与您的思维模型和整体系统架构相匹配,它可以成为避免显式刷新的有趣方法。不过,除非这个概念真正吸引您,否则我会把它放在最后考虑。