flaskbootstrap-5flask-wtformshtmx

Handling Flask WTForms Validation errors inside a bootstrap modal


I have dashboard endpoint where the user can view,edit,delete a campaign/form.

      <button hx-get="{{ url_for('campaign_action' , campaign_id=campaign.id , username=username) }}" 
      hx-trigger="click" hx-target="#campaignContainer" hx-swap="innerHTML" 
      data-bs-toggle="modal" data-bs-target="#viewCampaignModal"
      class="btn btn-primary" type="button">View</button>

      <button hx-patch="{{ url_for('campaign_action' , campaign_id=campaign.id , username=username) }}" 
      hx-trigger="click" hx-target="#campaignContainer" hx-swap="innerHTML" 
      data-bs-toggle="modal" data-bs-target="#viewCampaignModal"
      class="btn btn-warning" type="button">Edit</button>

      <button hx-delete="{{ url_for('campaign_action' , campaign_id=campaign.id , username=username) }}" 
      hx-target="#campaign-{{ campaign.id }}" hx-swap="outerHTML" hx-confirm="Are you sure you want to delete this campaign?"
      class="btn btn-danger" type="button">Delete</button>

These buttons send get,patch,delete request accordingly, to campaign_action endpoint where I handle the request.

Also these buttons swap the content inside #campaignContainer and Bootstrap modal class is toggled on #viewCampaignModal.

<div class="modal fade" id="viewCampaignModal" tabindex="-1" aria-hidden="true" aria-labelledby="modal-title">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="modal-title">Edit Campaign</h5>
        <button class="btn-close" type="button" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body" id="campaignContainer">
      </div>
    </div>
  </div>
</div>

campaign_action endpoint return campaign_action.html back in response to the htmx requests for view (which sends a get request) and edit (which sends patch request).

@app.route(
    "/dashboard/<string:username>/campagin/<int:campaign_id>",
    methods=["GET", "POST", "PATCH", "DELETE"],
)
def campaign_action(username, campaign_id: int):
    populated_form = AddCampaign(obj=CampaignManager.get({"id": campaign_id}))
    disabled = False

    if request.method == "GET":
        disabled = True
    elif request.method == "PATCH":
        disabled = False
    elif request.method == "DELETE":
        CampaignManager.delete_campaign(campaign_id)
        return ""
    elif request.method == "POST" and populated_form.validate_on_submit():
        CampaignManager.update(populated_form.data, campaign_id)
        return redirect(url_for("dashboard", username=session["username"]))

    return render_template(
        "dashboard/campaign_action.html",
        form=populated_form,
        disabled=disabled,
        username=username,
        campaign_id=campaign_id,
    )

Now the problem am facing is,

Whenever the Flask-WTForm validation fails the form is rendered on a blank page. I know that all the conditions in campaign_action fail hence the form is rendered separately on blank page but what I want is to show the errors on the modal itself.


Solution

  • After many attempts of prompting chatgpt This solution worked perfectly for me.

    <form method="POST" action="{{ url_for('campaign_action' ,username=username , campaign_id=campaign_id) }}" id="#modalForm"
    hx-post="{{ url_for('campaign_action' , username=username , campaign_id=campaign_id) }}"
    hx-target="#campaignContainer" 
    hx-swap= "innerHTML"
    >
    

    The form post method sends a request to campaign_action endpoint for validation of form. If the form is not validated then, the form is again rendered , post request made from htmx inserts this current form (along with the errors) again into the dashboard.html modal. Which updates the modal and shows all the validation errors.

    If the form is validated after making the post request on submission then we return an empty string along with an hx redirect.

    return ("",{"HX-Redirect": url_for("dashboard", username=session["username"])})
    

    HX-Redirect is necessary. Flask redirect(url_for('dashboard')) does not work. Using Flask redirect results in rendering the dashboard inside the modal!!!

    Why is that? I am not sure myself.