htmx-oob-swap not replacing <tr> (table row) fragments
Given the following starting web page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>HTMX Test</title>
<script
src="https://unpkg.com/[email protected]"
integrity="sha384-wg5Y/JwF7VxGk4zLsJEcAojRtlVp1FKKdGy1qN+OMtdq72WRvX/EdRdqg/LOhYeV"
crossorigin="anonymous"
></script>
<script>
htmx.config.useTemplateFragments = true;
</script>
</head>
<body>
<div
id="test"
hx-get="/htmx-test2"
hx-trigger="load delay:1s"
hx-swap="outerHTML"
>
<p>Test BEFORE swap</p>
</div>
<hr />
<h2>Contacts</h2>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody id="contacts-table">
<tr id="tr-test">
<td>Cell 1a</td>
<td>Cell 1b</td>
</tr>
</tbody>
</table>
</body>
</html>
with the following response returned from the /htmx-test2 endpoint after 1s:
<div id="test">
<p>Test AFTER swap</p>
</div>
<tr hx-swap-oob="beforeend:#contacts-table">
<td>Joe Smith</td>
<td>[email protected]</td>
</tr>
The <div> with id="test" gets swapped, as expected. However, the OOB <tr> fragment does not get replaced. The console in the Chrome Dev Tools doesn't show the OOB swap firing. Using afterbegin instead of beforeend doesn't work either. Even this doesn't work:
<div id="test">
<p>Test AFTER swap</p>
</div>
<tr id="tr-test" hx-swap-oob="true">
<td>Joe Smith</td>
<td>[email protected]</td>
</tr>
In all three cases, the whole content from the response replaces the <div> with id="test".
Note that htmx.config.useTemplateFragments is indeed set to true, as prescribed.
This test is quasi-identical to the example given at https://htmx.org/examples/update-other-content/#oob.
Turns out the https://htmx.org/examples/update-other-content/#oob example shows the <tr> tag portion is at the beginning. My example put it at the end. After more testing I realized the order does matter. Perhaps the instructions could be amended to specify that the order does matter, at least in the case of a <tr> tag.
While using the hx-target attribute and the hx-swap-oob attribute in my response, I encountered an issue where <tr> <td> tags were not processed by htmx. However, I managed to resolve it by simply moving the hx-swap-oob div to the bottom of the response.
For me, that doesn't work in either case.
I have the following root HTML
<table class="table table-striped">
<thead>
<tr>
<th scope="col">First Name</th>
<th scope="col">Last Name</th>
</tr>
</thead>
<tbody id="person-table">
</tbody>
</table>
And the following HTML reponse for the HTMX response:
<tr hx-swap-oob="beforeend:#person-table">
<td>John</td>
<td>Doe</td>
</tr>
<form id="personForm" class="row g-3">
<div class="col-md-4">
<label class="form-label" for="firstName">First name</label>
<input class="form-control" id="firstName" type="text" name="firstName" value="">
</div>
<div class="col-md-4">
<label class="form-label" for="lastName">Last name</label>
<input class="form-control" id="lastName" type="text" name="lastName" value="">
</div>
<div class="col-12">
<button class="btn btn-primary" type="submit" hx-target="#personForm" hx-swap="outerHTML"
hx-post="/form-validation">Submit form
</button>
</div>
</form>
You might ignore the form here, the important part is the <tr> part. When swapping that, I get the following table:
<table class="table table-striped">
<thead>
<tr>
<th scope="col">First Name</th>
<th scope="col">Last Name</th>
</tr>
</thead>
<tbody id="person-table">
<td>John</td>
<td>Doe</td>
</tbody>
</table>
So you see that the <tr> tag is missing.
When I change the order of the two snippt to add the
So, I'd suggest to reopen that ticket, thanks.
@radcool can you re-open that issue please? I can't unfortunately, and I would like to avoid creating a new one.
I just tried it again with exactly the example from https://htmx.org/examples/update-other-content/#oob and there is the same behaviour. The
Reopened as requested by @klu2.
Just found the more recent issue https://github.com/bigskysoftware/htmx/issues/1538, which seems to be a duplicate.
We can close either of the two but I would also state that this ticket here outlines the fact that the order of oob-fragments seems to matter - that is not mentioned in the documentation.
so we should either fix the fact that you get different results depending on the order of the fragments, or fix documentation and explain the rules.
I think there are more problems with the order of the oob fragments. I discovered that if you have multiple oob fragments being returned, it is important to place all oob table elements before any other oob elements. https://jsfiddle.net/zy03oque/ shows the behaviour - when the <td> is returned before the <output> it all works, but when the <output> is returned first, the <td> is not updated.
@akashchopra, does the behavior you observe seem in line with my comment @ https://github.com/bigskysoftware/htmx/issues/1198#issuecomment-1417903131?
@akashchopra, does the behavior you observe seem in line with my comment @ #1198 (comment)?
I think we are talking about slightly different things, but maybe I'm misunderstanding your example.
I'm referring to the order in which multiple oob fragments are given in the response. I understood your comment to be about whether a single oob fragment is before or after the main content.
@akashchopra I believe you're right. I didn't catch that you were referring to multiple OOB fragments. I just focused on the fact that it seems the order of table elements matters. Sorry about that.
I have a similar issue, I wraped the tr into a tbody and it worked (if I return the tbody as first element in the response)
Like this
<tbody hx-swap-oob="beforeend:#person-table"">
<tr>
<td>John</td>
<td>Doe</td>
</tr>
</tbody>
<form id="personForm" class="row g-3">
<div class="col-md-4">
<label class="form-label" for="firstName">First name</label>
<input class="form-control" id="firstName" type="text" name="firstName" value="">
</div>
<div class="col-md-4">
<label class="form-label" for="lastName">Last name</label>
<input class="form-control" id="lastName" type="text" name="lastName" value="">
</div>
<div class="col-12">
<button class="btn btn-primary" type="submit" hx-target="#personForm" hx-swap="outerHTML"
hx-post="/form-validation">Submit form
</button>
</div>
</form>
But my form is getting weird, after the submission the form looks like this:
<form id="personForm" class="row g-3">
</form>
<div class="col-md-4">
<label class="form-label" for="firstName">First name</label>
<input class="form-control" id="firstName" type="text" name="firstName" value="">
</div>
<div class="col-md-4">
<label class="form-label" for="lastName">Last name</label>
<input class="form-control" id="lastName" type="text" name="lastName" value="">
</div>
<div class="col-12">
<button class="btn btn-primary" type="submit" hx-target="#personForm" hx-swap="outerHTML"
hx-post="/form-validation">Submit form
</button>
</div>
This should be fixed by #1794.
I read #1538 and #1900. All related, same issue.
Is there a workaround for this except "Expand the Target" solution?
Edit: I fixed the issue with some little tweaks.
this Stack Overflow question was very helpful.
Here how I done it.
Set useTemplateFragments to true.
htmx.config.useTemplateFragments = true;
My response was like this:
<form hx-post="/contacts">
<label>
Name
<input name="name" type="text">
</label>
<label>
Email
<input name="email" type="email">
</label>
</form>
<tr hx-swap-oob="beforeend:#contacts-table">
<td>Joe Smith</td>
<td>[email protected]</td>
</tr>
I moved the OOB swapping part before the actual swapping part:
<tr hx-swap-oob="beforeend:#contacts-table">
<td>Joe Smith</td>
<td>[email protected]</td>
</tr>
<form hx-post="/contacts">
<label>
Name
<input name="name" type="text">
</label>
<label>
Email
<input name="email" type="email">
</label>
</form>
And finally, added the tbody tag and move hx-swap-oob attribute to it:
<tbody hx-swap-oob="beforeend:#contacts-table">
<tr>
<td>Joe Smith</td>
<td>[email protected]</td>
</tr>
</tbody>
<form hx-post="/contacts">
<label>
Name
<input name="name" type="text">
</label>
<label>
Email
<input name="email" type="email">
</label>
</form>