HTMX hx-swap-oob 添加新的表行只添加 <td> 而不是包裹的 <tr>。

huangapple go评论46阅读模式
英文:

HTMX hx-swap-oob adding a new table row only adds the <td>'s and not the wrapping <tr>

问题

以下是翻译好的部分:

问题

问题显然出在我身上。我正在学习HTMX,这可能是一个非常简单的错误。我在使用hx-swap-oob时遇到问题,它将响应中的<tr>标签剥离并简单地插入表格列到应该放置的行中。

我在服务器端使用Python Flask。我在样式上使用了Bootstrap 5,没有其他JS。

我试图构建一个“小部件CRUD”,并且正在遵循更新其他内容页面上的示例。它在大多数情况下正常工作,除了这个令人沮丧的异常。

这就是问题所在:

<tr>...</tr>
<tr>...</tr>
<th scope="row">New Widget</th>
<td>123</td>
<td>$123</td>
<td>
    <button class="btn btn-warning" hx-get="/widget/{{ id }}">Edit</button>
    <button class="btn btn-danger" hx-delete="/widget/{{ id }}">Delete</button>
</td>
</table>

如果有<tr>标签,它将完美无缺:-(

我还在使用编辑行删除行的示例,所以可能存在冲突?

相关代码

表格

<table class="table table-dark table-striped">
  <thead>
    <tr>
      <th scope="col">Name</th>
      <th scope="col">Qty</th>
      <th scope="col">Price</th>
      <th scope="col">Actions</th>
    </tr>
  </thead>
  <tbody hx-target="closest tr" hx-swap="outerHTML" id="widgets-table">
    {% for widget in widgets %}
    <tr>
      <th scope="row">{{ widget.name }}</th>
      <td>{{ widget.qty }}</td>
      <td>${{ widget.price / 100 }}</td>
      <td>
        <button class="btn btn-warning"
                hx-get="/widget/{{ widget.id }}">
          Edit
        </button>
        <button class="btn btn-danger"
                hx-delete="/widget/{{ widget.id }}"
                hx-confirm="Are you sure?">
          Delete
        </button>
      </td>
    </tr>
    {% endfor %}
  </tbody>
</table>

添加小部件表单

<div class="row mb-3" id="add-form">
  <div class="col">
    <h3>Add new Widget</h3>
    <form hx-post="/save">
    <div class="mb-3">
      <label for="name" class="form-label">Name</label>
      <input type="text" class="form-control" id="name" name="name">
    </div>
    <div class="mb-3">
      <label for="name" class="form-label">Qty</label>
      <input type="text" class="form-control" id="qty" name="qty">
    </div>
    <div class="mb-3">
      <label for="name" class="form-label">Price</label>
      <input type="text" class="form-control" id="price" name="price">
    </div>
    <input type="submit" class="btn btn-primary" value="Save New Widget">
    </form>
  </div>
</div>

示例中没有包括表单提交按钮,所以我在这里添加了<input type="submit">。也许我在这里漏掉了什么?

服务器响应模板

<tr hx-swap-oob="beforeend:#widgets-table">
  <th scope="row">{{ name }}</th>
  <td>{{ qty }}</td>
  <td>${{ price / 100 }}</td>
  <td>
    <button class="btn btn-warning" hx-get="/widget/{{ id }}">Edit</button>
    <button class="btn btn-danger" hx-delete="/widget/{{ id }}">Delete</button>
  </td>
</tr>
<div hx-swap-oob="outerHTML" id="add-form"></div>

我已经尝试随机添加hx-*标签到各处。只是缺少一些关于所有这些应该如何工作的关键信息。提前感谢您的帮助。

英文:

The Issue

The issue is obviously me. I'm learning HTMX and this is probably a really simple mistake. I'm having an issue with hx-swap-oob striping the &lt;tr&gt; tags out of the response and simply inserting the table columns where the row should go.

I'm using Python Flask on the server side. I'm using Bootstrap 5 for style. No other JS.

I'm attempting to build a "Widgets CRUD" and I'm following the example from the site for Updating Other Content. It's mostly working with this one sad exception.

Here is the issue:

&lt;tr&gt;...&lt;/tr&gt;
&lt;tr&gt;...&lt;/tr&gt;
&lt;th scope=&quot;row&quot;&gt;New Widget&lt;/th&gt;
&lt;td&gt;123&lt;/td&gt;
&lt;td&gt;$123&lt;/td&gt;
&lt;td&gt;
    &lt;button class=&quot;btn btn-warning&quot; hx-get=&quot;/widget/{{ id }}&quot;&gt;Edit&lt;/button&gt;
    &lt;button class=&quot;btn btn-danger&quot; hx-delete=&quot;/widget/{{ id }}&quot;&gt;Delete&lt;/button&gt;
&lt;/td&gt;
&lt;/table&gt;

If the &lt;tr&gt; tags were there it'd be perfect HTMX hx-swap-oob 添加新的表行只添加 <td> 而不是包裹的 <tr>。

I am also using the examples for Editing a row and Deleting a row so it's possible there is a conflict there?

Relevant code

The Table

&lt;table class=&quot;table table-dark table-striped&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th scope=&quot;col&quot;&gt;Name&lt;/th&gt;
      &lt;th scope=&quot;col&quot;&gt;Qty&lt;/th&gt;
      &lt;th scope=&quot;col&quot;&gt;Price&lt;/th&gt;
      &lt;th scope=&quot;col&quot;&gt;Actions&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody hx-target=&quot;closest tr&quot; hx-swap=&quot;outerHTML&quot; id=&quot;widgets-table&quot;&gt;
    {% for widget in widgets %}
    &lt;tr&gt;
      &lt;th scope=&quot;row&quot;&gt;{{ widget.name }}&lt;/th&gt;
      &lt;td&gt;{{ widget.qty }}&lt;/td&gt;
      &lt;td&gt;${{ widget.price / 100 }}&lt;/td&gt;
      &lt;td&gt;
        &lt;button class=&quot;btn btn-warning&quot;
                hx-get=&quot;/widget/{{ widget.id }}&quot;&gt;
          Edit
        &lt;/button&gt;
        &lt;button class=&quot;btn btn-danger&quot;
                hx-delete=&quot;/widget/{{ widget.id }}&quot;
                hx-confirm=&quot;Are you sure?&quot;&gt;
          Delete
        &lt;/button&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
    {% endfor %}
  &lt;/tbody&gt;
&lt;/table&gt;

The Add Widget Form

&lt;div class=&quot;row mb-3&quot; id=&quot;add-form&quot;&gt;
  &lt;div class=&quot;col&quot;&gt;
    &lt;h3&gt;Add new Widget&lt;/h3&gt;
    &lt;form hx-post=&quot;/save&quot;&gt;
    &lt;div class=&quot;mb-3&quot;&gt;
      &lt;label for=&quot;name&quot; class=&quot;form-label&quot;&gt;Name&lt;/label&gt;
      &lt;input type=&quot;text&quot; class=&quot;form-control&quot; id=&quot;name&quot; name=&quot;name&quot;&gt;
    &lt;/div&gt;
    &lt;div class=&quot;mb-3&quot;&gt;
      &lt;label for=&quot;name&quot; class=&quot;form-label&quot;&gt;Qty&lt;/label&gt;
      &lt;input type=&quot;text&quot; class=&quot;form-control&quot; id=&quot;qty&quot; name=&quot;qty&quot;&gt;
    &lt;/div&gt;
    &lt;div class=&quot;mb-3&quot;&gt;
      &lt;label for=&quot;name&quot; class=&quot;form-label&quot;&gt;Price&lt;/label&gt;
      &lt;input type=&quot;text&quot; class=&quot;form-control&quot; id=&quot;price&quot; name=&quot;price&quot;&gt;
    &lt;/div&gt;
    &lt;input type=&quot;submit&quot; class=&quot;btn btn-primary&quot; value=&quot;Save New Widget&quot;&gt;
    &lt;/form&gt;
  &lt;/div&gt;
&lt;/div&gt;

The example doesn't include the form submit button so I added that &lt;input type=&quot;submit&quot;&gt; in there. Maybe I'm missing something there?

Server response template

&lt;tr hx-swap-oob=&quot;beforeend:#widgets-table&quot;&gt;
  &lt;th scope=&quot;row&quot;&gt;{{ name }}&lt;/th&gt;
  &lt;td&gt;{{ qty }}&lt;/td&gt;
  &lt;td&gt;${{ price / 100 }}&lt;/td&gt;
  &lt;td&gt;
    &lt;button class=&quot;btn btn-warning&quot; hx-get=&quot;/widget/{{ id }}&quot;&gt;Edit&lt;/button&gt;
    &lt;button class=&quot;btn btn-danger&quot; hx-delete=&quot;/widget/{{ id }}&quot;&gt;Delete&lt;/button&gt;
  &lt;/td&gt;
&lt;/tr&gt;
&lt;div hx-swap-oob=&quot;outerHTML&quot; id=&quot;add-form&quot;&gt;&lt;/div&gt;

I've tried randomly sprinkling hx-* tags everywhere. Just missing a key something about how all this is supposed to work. Thanks in advance.

答案1

得分: 3

尝试将响应中的行包装在tbody中,如下所示:

&lt;tbody hx-swap-oob=&quot;beforeend:#widgets-table&quot;&gt;
    &lt;tr id=&quot;row2&quot;&gt;
        &lt;td&gt;来自响应的单元格内部&lt;/td&gt;
        &lt;td&gt;来自响应的单元格内部&lt;/td&gt;
    &lt;/tr&gt;
&lt;/tbody&gt;

这应该使HTML解析器对表格结构更满意,而且htmx将能够正确找到OOB内容。可能会有一些边缘情况需要考虑,但通常情况下这应该有所帮助。

英文:

html parser is uncharacteristically strict about parsing table structure, which is especially painful with OOB elements that must have no parent

Try wrapping your row in the response in tbody like so

&lt;tbody hx-swap-oob=&quot;beforeend:#widgets-table&quot;&gt;
    &lt;tr id=&quot;row2&quot;&gt;
        &lt;td&gt;internals of cells from response&lt;/td&gt;
        &lt;td&gt;internals of cells from response&lt;/td&gt;
    &lt;/tr&gt;
&lt;/tbody&gt;

This should make html parser a bit happier about table structure and htmx will be able to find OOB content correctly. There might be some edge cases with that solution, but generally it should help

huangapple
  • 本文由 发表于 2023年7月4日 20:33:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/76612680.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定