Reputation: 369
<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
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
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
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
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
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
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
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