jgirardet
jgirardet

Reputation: 369

handle errors with HTMX

<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 ?

Upvotes: 26

Views: 23612

Answers (7)

xpmatteo
xpmatteo

Reputation: 11408

This seems to work:

<p id="error" style="background: pink"></p>

<script>
    htmx.on("htmx:responseError", function (evt) {
        document.getElementById('error').innerHTML = evt.detail.error
    })
    htmx.on("htmx:sendError", function (evt) {
        document.getElementById('error').innerHTML = evt.detail.error
    })
    // clear the error message when starting the next interaction
    htmx.on("htmx:beforeSend", function () {
        document.getElementById('error').innerHTML = ""
    })
</script>

Edit: this can be upgraded to

/*
This will make errors generated by htmx visible.
It assumes there is an element with id "error"

See https://htmx.org/events/
*/
htmx.on("htmx:responseError", function (evt) {
    const requestConfig = evt.detail.requestConfig;
    const xhr = evt.detail.xhr;
    document.getElementById('error').innerHTML = `<b>${requestConfig.verb} ${requestConfig.path}</b> returned <b>${xhr.status} ${xhr.statusText}</b><br>${xhr.responseText}`
})
htmx.on("htmx:sendError", function (evt) {
    const requestConfig = evt.detail.requestConfig;
    document.getElementById('error').innerHTML = `Network error on <b>${requestConfig.verb} ${requestConfig.path}</b>`
})
htmx.on("htmx:targetError", function (evt) {
    document.getElementById('error').innerHTML = `Target error: in ${evt.detail.element}, offending selector ${evt.detail.target}`
})
htmx.on("htmx:beforeSend", function () {
    document.getElementById('error').innerHTML = ""
})

Upvotes: 1

AlexTT
AlexTT

Reputation: 1003

In simple cases, you can use hx-on:htmx:response-error.

<form hx-post="/no-route" hx-target="#result" hx-on:htmx:response-error="alert('error')">
     <button type="submit" class="btn btn-success">Submit</button>
</form> 
<div id="result"></div>

Or another option is to remove the hidden class from the error message:

<button hx-get="/some-address" hx-on:htmx:response-error="htmx.removeClass('#error', 'hidden')">
      Fetch 
</button>
<div id="error" class="hidden">
      Error! 
</div>

This doesn't require any additional extensions, and in fact, you don't need anything else at all, it's all in one line.

Example: https://jsfiddle.net/1f4d37ma/

Upvotes: 1

siefca
siefca

Reputation: 1147

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>

Upvotes: 15

Mueller-Nico
Mueller-Nico

Reputation: 476

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.

Upvotes: 22

Van Hudson Galvoso
Van Hudson Galvoso

Reputation: 86

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
});

Upvotes: 2

Breno Silva
Breno Silva

Reputation: 301

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";
  }
});

Upvotes: 7

Ben Pate
Ben Pate

Reputation: 336

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.

Upvotes: 2

Related Questions