I am experimenting with flask and just creating some basic functionality to add data to a form displayed in a modal. While I have managed to get the form to display in the modal and save it from the modal, I am struggling to understand what needs to be done to ensure that field validation errors are shown on the modal itself. Currently if there are errors user is redirected to a whole page with edit form.
They say picture is better than words - So here is a gif showing what is happening:
The entire app code is on github and the current state on heroku can be accessed here... username: [email protected] and password: adminpassword. It's all sandbox anyway.
Relevant code is as below:
routes.py
@expenses.route("/expense")
@login_required
def expense():
page = request.args.get('page', 1, type=int)
expenses = Expense.query.order_by(Expense.expense_date.desc()).paginate(page=page, per_page=5)
form = ExpenseForm()
return render_template('expense/expense.html', expenses=expenses, form=form)
@expenses.route("/expense/new", methods=['GET', 'POST'])
@login_required
def new_expense():
form = ExpenseForm()
if form.validate_on_submit():
expense = Expense(description=form.description.data, expense_date=form.expense_date.data,
amount=form.amount.data,vat_amount=form.vat_amount.data,Transferrable=form.Transferrable.data, author=current_user)
db.session.add(expense)
db.session.commit()
flash('Your expense has been created!', 'success')
return redirect(url_for('expenses.expense'))
return render_template('expense/create_expense.html', title='New Expense',
form=form, legend='New Expense')
Now the expenses.html is a big one but on it the modal is called using the following:
<button type="button" class="btn btn-primary btn-sm m-1" data-toggle="modal" data-target="#AddNewModal">Add New Expense</button>
{% include "expense/partials/addModal.html" %}
and the addModal.html is as shown below:
<!-- Add New Modal -->
{% from "util/macros.html" import form_field with context %}
<div class="modal fade" id="AddNewModal" tabindex="-1" role="dialog" aria-labelledby="AddNewModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="AddNewModalLabel">Add New Expense</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="amount-section">
<form method="POST" action="/expense/new">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">{{ legend }}</legend>
<div class="form-group">
{{ form.description.label(class="form-control-label") }}
{% if form.description.errors %}
{{ form.description(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.description.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.description(class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ form.amount.label(class="form-control-label") }}
{% if form.amount.errors %}
{{ form.amount(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.amount.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.amount(class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ form.expense_date.label(class="form-control-label") }}
{% if form.expense_date.errors %}
{{ form.expense_date(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.expense_date.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.expense_date(class="form-control form-control-lg", type="date") }}
{% endif %}
</div>
<div class="form-group">
{{ form.vat_amount.label(class="form-control-label") }}
{% if form.vat_amount.errors %}
{{ form.vat_amount(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.vat_amount.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.vat_amount(class="form-control form-control-lg") }}
{% endif %}
</div>
<!-- {{ form_field(form.vat_amount,with_label=True) }} -->
<div class="form-group">
{% if form.Transferrable.errors %}
{{ form.Transferrable(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.Transferrable.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.Transferrable(type="checkbox") }}
{% endif %}
{{ form.Transferrable.label(class="form-control-label") }}
</div>
<!-- {{ form_field(form.Transferrable) }} -->
<p><button type="submit" class="btn btn-primary">Add</button></p>
</fieldset>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
It is because if form has some errors you render create_expense.html
that's why form is presented not in modal.
I would merge 2 views: /expense/new
and /expense
so that it can handle both GET
and POST
and conditionally show modal if form has some errors.
Merged views:
@expenses.route("/expense", methods=['GET', 'POST'])
@login_required
def expense():
page = request.args.get('page', 1, type=int)
expenses = Expense.query.order_by(Expense.expense_date.desc()).paginate(page=page, per_page=5)
form = ExpenseForm()
if form.validate_on_submit():
expense = Expense(description=form.description.data, expense_date=form.expense_date.data,
amount=form.amount.data,vat_amount=form.vat_amount.data,Transferrable=form.Transferrable.data, author=current_user)
db.session.add(expense)
db.session.commit()
flash('Your expense has been created!', 'success')
return render_template('expense/expense.html', expenses=expenses, form=form)
Conditional modal showing at the bottom of addModal.html
:
{% if form.errors %}
<script>
$('#AddNewModal').modal('show');
</script>
{% endif %}
And action for the form in addModal.html
must be changed as well to:
<form method="POST" action="/expense">
However after these changes views /expense/new
and /expense
would have some code in common so refactoring might be needed. Now you at least know why errors are not shown in the modal.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With