When using HX-Redirect, the htmx:afterRequest event's detail.successful and detail.failed fields are undefined
When the server returns a HX-Redirect header, the htmx:afterRequest event for that request will not tell whether the request was successful or failed. The event.detail.successful and event.detail.failed are both undefined.
The expected behavior would be that the successful and failed fields will always be present.
On related note, the fields are also missing if the browser is in offline mode. I think those too should be categorized as failed requests.
This happens with htmx 1.9.12 and 2.0.0-beta3
Reproducing
from flask import Flask, Response, request
app = Flask(__name__)
@app.route("/")
def home():
return """
<script src="https://unpkg.com/[email protected]"></script>
<script type="module">
document.body.addEventListener('htmx:afterRequest', event => {
console.log(event);
console.log('status: ' + event.detail.xhr.status);
console.log('successful: ' + event.detail.successful);
console.log('failed: ' + event.detail.failed);
// Some error handling code:
if (event.detail.successful) {
console.log('Request successful.');
} else if (event.detail.failed) {
console.log('Request failed.');
alert('Request failed.');
} else {
console.log('Unknown error. Probably network is down.');
alert('Unknown error. Probably network is down.');
}
});
</script>
<h1>Home</h1>
<p><button hx-get="/clicked?scenario=1" hx-swap="outerHTML">Button 1: successful</button></p>
<p><button hx-get="/clicked?scenario=2" hx-swap="outerHTML">Button 2: error from backend</button></p>
<p><button hx-get="/clicked?scenario=3" hx-swap="outerHTML">Button 3: hx-redirect (BUG: thought to be a network error)</button></p>
<p><a href="/">Restart</a></p>
"""
@app.route("/clicked")
def clicked():
resp = Response("<h3>Clicked</h3>")
scenario = request.args.get('scenario')
if scenario == "1":
resp.status = 200
if scenario == "2":
resp.status = 400
if scenario == "3":
# BUG: If the HX-Redirect header is present, the successful/failed fields will be undefined.
resp.status = 200
resp.headers["HX-Redirect"] = "/redirected"
return resp
@app.route("/redirected")
def redirected():
return """
<h1>Redirected</h1>
<p><a href="/">Restart</a></p>
"""
Run the above app with the commands:
pip install Flask
flask --app server.py --debug run
Visit http://127.0.0.1:5000/ and click each of the buttons while observing what is printed to the console.
Buttons 1 and 2 work correctly.
But when you click Button 3, the htmx:afterRequest event will be missing the event.detail.successful and event.detail.failed fields. The app will print the following to the console and its error handler will show an error popup. (In a real app that doesn't use alert(), the error message would flash for a fraction of a second before redirecting to the next page).
status: 200
successful: undefined
failed: undefined
Those fields are also missing when the browser is in offline mode. (This article told to use that to detect network errors. I'm not sure if that is expected behavior.)
Set the browser to offline mode in the Chrome DevTool's Network tab and click any of the three buttons to see that behavior.
Workaround
Ignore the event.detail.successful and event.detail.failed fields, and instead check the HTTP status code in event.detail.xhr.status yourself.
Just observed same thing happening with HX-Location, on successful response event.detail.successful is missing
This is frustrating since looking for the successful response was one solution to #1925 (Keep hmtx-request class while redirecting). Should a redirect header not be a successful response?
Just bit me too :)