mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-09-30 14:31:47 +00:00
updating multiple pieces of content
This commit is contained in:
parent
cd59a86b2e
commit
f70825a2d5
@ -961,6 +961,7 @@ listed below:
|
||||
| `htmx.config.settlingClass` | defaults to `htmx-settling`
|
||||
| `htmx.config.swappingClass` | defaults to `htmx-swapping`
|
||||
| `htmx.config.allowEval` | defaults to `true`
|
||||
| `htmx.config.useTemplateFragments` | defaults to `false`, HTML template tags for parsing content from the server (not IE11 compatible!)
|
||||
| `htmx.config.wsReconnectDelay` | defaults to `full-jitter`
|
||||
| `htmx.config.disableSelector` | defaults to `[disable-htmx], [data-disable-htmx]`, htmx will not process elements with this attribute on it or a parent
|
||||
|
||||
|
@ -36,4 +36,4 @@ You can copy and paste them and then adjust them for your needs.
|
||||
| [Tabs (Using Hyperscript)](/examples/tabs-hyperscript) | Demonstrates how to display and select tabs using Hyperscript
|
||||
| [Keyboard Shortcuts](/examples/keyboard-shortcuts) | Demonstrates how to create keyboard shortcuts for htmx enabled elements
|
||||
| [Sortable](/examples/sortable) | Demonstrates how to use htmx with the Sortable.js plugin to implement drag-and-drop reordering
|
||||
|
||||
| [Updating Other Content](/examples/update-other-content) | Demonstrates how to update content beyond just the target elements
|
||||
|
240
www/examples/update-other-content.md
Normal file
240
www/examples/update-other-content.md
Normal file
@ -0,0 +1,240 @@
|
||||
---
|
||||
layout: demo_layout.njk
|
||||
---
|
||||
|
||||
## Updating Other Content
|
||||
|
||||
A common question that comes up when working with htmx is: "I need to update other content on the screen. How
|
||||
do I do this?" There are multiple ways to do so, and in this example will walk you through some of them.
|
||||
|
||||
We'll use the following basic UI to discuss this concept: a simple table of contacts and a form to add new contacts on the page.
|
||||
|
||||
The problem here is that, when you submit a new contact in the form, you want the contact table above to refresh.
|
||||
|
||||
```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>
|
||||
```
|
||||
|
||||
What solutions to we have?
|
||||
|
||||
### Solution 1: Expand the Target
|
||||
|
||||
The easiest solution here is to "expand the target" of the form to enclose both the table and the form. For example
|
||||
you could wrap the whole thing in a `div` and target that div:
|
||||
|
||||
```html
|
||||
<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>
|
||||
```
|
||||
|
||||
Note that we are targeting the enclosing div using the [hx-target](/attributes/hx-target) attribute.
|
||||
|
||||
This is a simple and reliable approach, although it might not feel the most elegant.
|
||||
|
||||
### Solution 2: Out of Band Responses
|
||||
|
||||
A more sophisticated approach to this problem would use [out of band swaps](/attributes/hx-swap-oob/) to swap in
|
||||
updated content to the DOM.
|
||||
|
||||
The HTML doesn't need to change from the original setup:
|
||||
|
||||
```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>
|
||||
```
|
||||
|
||||
However, in your response to the `POST` to `/contacts` you would respond with some additional content:
|
||||
|
||||
```html
|
||||
<tr hx-oob-swap="beforeend:#contacts-table">
|
||||
<td>Joe Smith</td>
|
||||
<td>joe@smith.com</td>
|
||||
</tr>
|
||||
<form hx-post="/contacts">
|
||||
<label>
|
||||
Name
|
||||
<input name="name" type="text">
|
||||
</label>
|
||||
<label>
|
||||
Email
|
||||
<input name="email" type="email">
|
||||
</label>
|
||||
</form>
|
||||
```
|
||||
|
||||
We use the [hx-oob-swap](/attributes/hx-oob-swap) attribute to append some new content into the `#contacts-table`.
|
||||
|
||||
Note that because we are using table rows here, we must enable template fragment parsing (thus sacrificing IE11 compatibility)
|
||||
|
||||
```javascript
|
||||
htmx.config.useTemplateFragments = true;
|
||||
```
|
||||
|
||||
### Solution 3: Triggering Events
|
||||
|
||||
An even more sophisticated approach would be to trigger a client side event when a successful contact is created and
|
||||
then listen for that event on the table, causing it to refresh.
|
||||
|
||||
```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-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>
|
||||
```
|
||||
|
||||
We have added a new end point `/contacts/table` that re-renders the contacts table. We have a trigger on a custom
|
||||
event and we listen for the event on the body of the page.
|
||||
|
||||
When a successful contact creation occurs during a POST to `/contacts`, the response includes an [HX-Trigger](https://htmx.org/headers/hx-trigger/) response header that looks like this:
|
||||
|
||||
```text
|
||||
HX-Trigger:newContact
|
||||
```
|
||||
|
||||
This will trigger the table it issue a `GET` to `/contacts/table` and refresh the table. Very clean, event
|
||||
driven programming!
|
||||
|
||||
### Solution 4: Using the Path Dependencies Extension
|
||||
|
||||
A final solution is to use REST-ful path dependencies to refresh the table. Intercooler.js, the predecessor
|
||||
to htmx, had [path-based dependencies](https://intercoolerjs.org/docs.html#dependencies) integrated into the
|
||||
library. htmx dropped this as a core feature, but supports an extension, [path deps](/extensions/path-deps/),
|
||||
that gives similar functionality.
|
||||
|
||||
Updating our example to use the extension would involve loading the extension javascript and then
|
||||
annotating our HTML like so:
|
||||
|
||||
```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>
|
||||
```
|
||||
|
||||
Now, when the form posts to the `/contacts` URL, the `path-deps` extension will detect that and trigger an `path-deps`
|
||||
event on the contacts table, therefore triggering a request.
|
||||
|
||||
The advantage here is that you don't need to do anything fancy with response headers. The downside is that a request
|
||||
will be issued on every `POST`, even if a contact was not successfully created.
|
||||
|
||||
### Which should I use?
|
||||
|
||||
I would recommend the first approach, expanding your target, if the elements that need to be updated are reasonably
|
||||
close to one another in the DOM. It is simple and reliable.
|
||||
|
||||
After that, I would say it is a tossup between a custom event and an OOB swap. I would lean towards the custom event
|
||||
because I like event-oriented systems, but that's a personal preference. Which one you choose should be dictated by you
|
||||
own software engineering tastes and what your server-side technology makes easy.
|
||||
|
||||
Finally, the path-deps approach is interesting, and if it fits well with your mental model and overall system architecture,
|
||||
it can be a fun way to avoid explicit refreshing. I would look at it last, however, unless the concept really grabs
|
||||
you.
|
Loading…
x
Reference in New Issue
Block a user