handle errors with HTMX
Asked Answered
O

5

23
<form  
 class="" id="form" hx-post="/add/" hx-swap="afterbegin" hx-target="#big_list" hx-trigger="submit">
    <input type="text" name="langue1" >
    <input type="text" name="langue2">
    <div id="errors"></div>
    <button type="submit">GO</button>
</form> 
<div id="big_list"> 
.....
</div>

I have a big list in #big_list, and I want my #form appends only one row when submitted.

How with htmx, can I handle errors and show message in #errors ?

Octal answered 28/9, 2021 at 15:9 Comment(5)
I mean : - If my request succeeds, the result is put in hx-target (here : #big_list) - But if my request fails (300, 400, HttpErrors....) how do I put the result in #errors but not in #big_list` ? That's what I meant.Octal
Sorry. I have not done this before. By chance I read this: twitter.com/htmx_org/status/1443316447942348805Ciceronian
Do these docs help? htmx.org/docs/#requests In the event of an error response from the server (e.g. a 404 or a 501), htmx will trigger the htmx:responseError event, which you can handle. In the event of a connection error, the htmx:sendError event will be triggered.Ciceronian
the author answered me : github.com/bigskysoftware/htmx/issues/607Octal
I love htmx, but I feel that missing a smooth way to handle HTTP status codes is the biggest flaw in the library.Moxie
J
16

If your code raises the errors (validation?), you can change target and swap behavior with response headers.

Response.Headers.Add("HX-Retarget", "#errors"); 
Response.Headers.Add("HX-Reswap", "innerHTML");

If you want to return a status other than 200, you have to tell htmx to accept it. 4xx would normally not do a swap in htmx. In case of validation errors you could use 422.

document.body.addEventListener('htmx:beforeOnLoad', function (evt) {
    if (evt.detail.xhr.status === 422) {
        evt.detail.shouldSwap = true;
        evt.detail.isError = false;
    }
});

It works in htmx 1.8.

If you want to remove the error message on then next sucessfull request, you could use hx-swap-oob. Out of band elements must be in the top level of the response. So the response could look like this:

<div>
    your new row data...
</div>
<div id="errors" hx-swap-oob="true"></div>

Update
You can now use the new powerful extension multi-swap to swap multiple elements arbitrarily placed and nested in the DOM tree.
See

Update
Take a look at the new extension The response-targets Extension.
This extension allows you to specify different target elements to be swapped when different HTTP response codes are received.

Jarad answered 5/9, 2022 at 22:50 Comment(1)
Ah, the response-targets extension sounds like something that should be first-thing in their main page, uhg.Progeny
B
12

See https://github.com/bigskysoftware/htmx-extensions/blob/main/src/response-targets/README.md

(May require htmx.js and response-targets.js downloaded from a GitHub repository until new version is released.)

<script src="https://unpkg.com/htmx.org/dist/ext/response-targets.js"></script>

<div hx-ext="response-targets">
    <div id="response-div"></div>
    <button hx-post="/register"
            hx-target="#response-div"
            hx-target-5*="#serious-errors"
            hx-target-404="#not-found">
        Register!
    </button>
    <div id="serious-errors"></div>
    <div id="not-found"></div>
</div>
Buckingham answered 1/6, 2023 at 0:2 Comment(1)
This is about the most suitable approach. However, at least version 1.9.5 of HTMX doesn't yet support hx-target-401, regardless if response-targets.js is used or not.Engleman
M
7

I created this solution so you can use hx-target-error = to define which HTML will be displayed after a failed request

document.body.addEventListener('htmx:afterRequest', function (evt) {
  const targetError = evt.target.attributes.getNamedItem('hx-target-error')
  if (evt.detail.failed && targetError) {
    document.getElementById(targetError.value).style.display = "inline";
  }
});
document.body.addEventListener('htmx:beforeRequest', function (evt) {
  const targetError = evt.target.attributes.getNamedItem('hx-target-error')
  if (targetError) {
    document.getElementById(targetError.value).style.display = "none";
  }
});
Mcclanahan answered 7/12, 2021 at 14:44 Comment(0)
M
2

Although it doesn't follow REST principles, you might consider using an swap-oob to report your error back to your user. For example, your request might return a (slightly misleading) status 200, but include content like this:

<div id="errors" hx-swap-oob="true">
  There was an error processing your request...
</div>

If it's important to follow REST more precisely, then you'll want to listen to the htmx:responseError event, as mentioned by @guettli in his previous answer.

Mccain answered 14/10, 2021 at 0:40 Comment(0)
M
2

It's not complicated than you think, just use htmx events and a little js.

Add htmx:responseError event listener to the form and pass the error details to #error

document.getElementById('form').addEventListener('htmx:responseError', function(evt) {
    document.getElementById('error').innerHTML = evt.detail.error
});
Megasporangium answered 22/8, 2023 at 9:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.