Flask: bootstrap modal w/ form validation

When I coding my first Flask application, I was faced with challenge to get working user details editing form in the bootstrap modal with proper form validation from every page (I want to place “Edit profile” button in the top menu). I found good article, but JS from it works well only if modal opens on same route that’s form are placed. So I spend a whole day to change this JS and as a result, it had some improvements.

FYI, this solution is applicable if you used Flask, wtforms and bootstrap. Lets start from form route example.

import json
from flask import render_template, request, jsonify

@app.route('/user/<id>/edit', methods=['GET', 'POST'])
def user_edit(id):
    user = User.query.filter_by(id=id).first_or_404()
    form = ProfileEditForm(user.email)
    if form.validate_on_submit():
        user.email = form.email.data
        db.session.commit()
        return jsonify(status='ok')
    elif request.method == 'GET':
        form.email.data = user.email
    else:
        data = json.dumps(form.errors, ensure_ascii=False)
        return jsonify(data)
    return render_template('_form_edit.html', title="Редактирование пользователя", form=form)

Some explanations.

  • If GET – return form with data using form template _form_edit.html
  • If POST (and validation succeed) – save the form and return json “ok”
  • If form validation failed – catch wtforms errors to json and return it.

Now “front-end” template files. Add Initial modal html into base.html (file where you placed page header)

<!-- Dynamic Modal -->
<div class="modal fade" id="Modal" tabindex="-1" role="dialog" aria-labelledby="FormModal" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
         <!-- load content here -->
        </div>
    </div>
</div>
<!-- End Dynamic Modal -->

A button that opens modal window. You can place it on any page (I added to the header nav-menu).

<a class="edit-modal-opener" data-toggle="modal"
                            data-whatever="{{ url_for('user_edit', id=user.id) }}" href="#">Изменить</a>

And modal-content (form) _form_edit.html

{% from 'bootstrap/wtf.html' import form_field %}
<form id="ModalForm" name="stepForm" class="form" method="post">
    <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="close">
            <span aria-hidden="true">&times;</span>
        </button>
        <h4 class="modal-title">{{ title }}</h4>
    </div>
    <div class="modal-body">

        {{ form.hidden_tag() }}
        {{ form_field(form.email) }}

    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Отмена</button>
        <button id="submit" type="submit" class="btn btn-success">Сохранить</button>
    </div>
</form>

And most important in that case, javascript that operate all modal logic. Place it into your’s base.html (file contains <head>) to the <script> section.

$(document).ready(function () {
    $('.edit-modal-opener').click(function () {
        var url = $(this).data('whatever');
        $.get(url, function (data) {
            $('#Modal .modal-content').html(data);
            $('#Modal').modal();

            $('#submit').click(function (event) {
                event.preventDefault();
                $.post(url, data = $('#ModalForm').serialize(), function (
                    data) {
                    if (data.status == 'ok') {
                        $('#Modal').modal('hide');
                        location.reload();
                    } else {
                        var obj = JSON.parse(data);
                        for (var key in obj) {
                            if (obj.hasOwnProperty(key)) {
                                var value = obj[key];
                            }
                        }
                        $('.help-block').remove()
                        $('<p class="help-block">' + value + '</p>')
                            .insertAfter('#' + key);
                        $('.form-group').addClass('has-error')
                    }
                })
            });
        })
    });
});

That’s is all. All form requests going background, modal window closes only if form was validate successful. If flask-wtf return some errors, JS catch it and add error classes to the form fields.

2 comments on “Flask: bootstrap modal w/ form validation

  1. Would it be ok to share the full code. There is reference for {% from ‘bootstrap/wtf.html’ import form_field %} and what exactly data-whatever is?
    Изменить

    Apologise fairly new to the coding world so asking stupid question.

Leave a Reply

Your email address will not be published. Required fields are marked *