Search code examples
djangohtmx

How to prevent hx-push-url when form (triggered from outside itself) does not validate


I am building a multi-step form-wizard. When posting each step of the form, I want the url to change. For example 1st step has a url like /update/1/, and when I click Continue, the 2nd step will be at /update/2/ and so on. This is useful so that in case the user does a hard refresh, I can still "stay" in the same step. I deal with this in my view when request.method=="GET"

I have a form for which hx-post is triggered by elements outside it like so:

<div id="form-wrap">
  <form id="my-form">
    <input hx-validate="true" some-input.../> {# all inputs have hx-validate="true" #}
  </form>
</div>
<button hx-post="{% url 'my_app:company-update' prev %}" hx-include="#my-form *"  hx-target="#form-wrap" hx-vals='{ "posting_step": {{current_step}}}' hx-push-url="true">Back</button>
<button hx-post="{% url 'my_app:company-update' next %}" hx-include="#my-form *"  hx-target="#form-wrap" hx-vals='{ "posting_step": {{current_step}}}' hx-push-url="true" >Continue</button>

When the form is valid, everything works fine. However, when any of the fields has validation errors, hx-push-url still pushes the url resulting in seeing the same step of the form with errors, but the url does not correspond to that step, but the next one... Perhaps this is related to this. Is there anyway to go around this?

The part of my view dealing with this:

# views.py
# ...
else:  # form invalid
    context = {"current_step": posting_step}
    context["form"] = form_posted
    return render(request, "my_app/_company_form.html", context)

Thanks


Solution

  • HTMX supports pushing an URL via the HX-Push-Url header as well. So remove hx-push-url="true" from the buttons and set the push URL at the view function depending on the form validation:

    context = {"current_step": posting_step}
    context["form"] = form_posted
    
    # Get subsequent step's URL
    step_url = reverse('my_app:company-update', args=[posting_step])
    
    # Get response object in order to set headers
    response = render(request, "my_app/_company_form.html", context)
    
    # Set HX-Push-Url header
    response['HX-Push-Url'] = step_url
    
    return response