Skip to content

Instantly share code, notes, and snippets.

@rmed rmed/app.py
Created Mar 2, 2019

Embed
What would you like to do?
Dynamic Flask-WTF fields
# -*- coding: utf-8 -*-
# app.py
from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import Form, FieldList, FormField, IntegerField, StringField, \
SubmitField
class LapForm(Form):
"""Subform.
CSRF is disabled for this subform (using `Form` as parent class) because
it is never used by itself.
"""
runner_name = StringField('Runner name')
lap_time = IntegerField('Lap time')
class MainForm(FlaskForm):
"""Parent form."""
laps = FieldList(
FormField(LapForm),
min_entries=1,
max_entries=20
)
app = Flask(__name__)
app.config['SECRET_KEY'] = 'sosecret'
@app.route('/', methods=['GET', 'POST'])
def index():
form = MainForm()
if form.validate_on_submit():
print(form.data)
# Display form data
return render_template(
'index.html',
form=form,
data=form.data
)
return render_template(
'index.html',
form=form
)
if __name__ == '__main__':
app.run()
{# templates/index.html #}
<html>
<head>
<title>Lap logging</title>
{# Import JQuery #}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
/**
* Adjust the indices of form fields when removing items.
*/
function adjustIndices(removedIndex) {
var $forms = $('.subform');
$forms.each(function(i) {
var $form = $(this);
var index = parseInt($form.data('index'));
var newIndex = index - 1;
if (index < removedIndex) {
// Skip
return true;
}
// Change ID in form itself
$form.attr('id', $form.attr('id').replace(index, newIndex));
$form.data('index', newIndex);
// Change IDs in form inputs
$form.find('input').each(function(j) {
var $item = $(this);
$item.attr('id', $item.attr('id').replace(index, newIndex));
$item.attr('name', $item.attr('name').replace(index, newIndex));
});
});
}
/**
* Remove a form.
*/
function removeForm() {
var $removedForm = $(this).closest('.subform');
var removedIndex = parseInt($removedForm.data('index'));
$removedForm.remove();
// Update indices
adjustIndices(removedIndex);
}
/**
* Add a new form.
*/
function addForm() {
var $templateForm = $('#lap-_-form');
if (!$templateForm) {
console.log('[ERROR] Cannot find template');
return;
}
// Get Last index
var $lastForm = $('.subform').last();
var newIndex = 0;
if ($lastForm.length > 0) {
newIndex = parseInt($lastForm.data('index')) + 1;
}
// Maximum of 20 subforms
if (newIndex > 20) {
console.log('[WARNING] Reached maximum number of elements');
return;
}
// Add elements
var $newForm = $templateForm.clone();
$newForm.attr('id', $newForm.attr('id').replace('_', newIndex));
$newForm.data('index', newIndex);
$newForm.find('input').each(function(idx) {
var $item = $(this);
$item.attr('id', $item.attr('id').replace('_', newIndex));
$item.attr('name', $item.attr('name').replace('_', newIndex));
});
// Append
$('#subforms-container').append($newForm);
$newForm.addClass('subform');
$newForm.removeClass('is-hidden');
$newForm.find('.remove').click(removeForm);
}
$(document).ready(function() {
$('#add').click(addForm);
$('.remove').click(removeForm);
});
</script>
<style>
.is-hidden {
display: none;
}
</style>
</head>
<body>
<a id="add" href="#">Add Lap</a>
{# Show all subforms #}
<form id="lap-form" action="" method="POST" role="form">
{{ form.hidden_tag() }}
<div id="subforms-container">
{% for subform in form.laps %}
<div id="lap-{{ loop.index0 }}-form" class="subform" data-index="{{ loop.index0 }}">
{{ subform.runner_name.label }}
{{ subform.runner_name }}
{{ subform.lap_time.label }}
{{ subform.lap_time}}
<a class="remove" href="#">Remove</a>
</div>
{% endfor %}
</div>
<button type="submit">Send</button>
</form>
{% if form.errors %}
{{ form.errors }}
{% endif %}
{# Form template #}
<div id="lap-_-form" class="is-hidden" data-index="_">
<label for="laps-_-runner_name">Runner name</label>
<input id="laps-_-runner_name" name="laps-_-runner_name" type="text" value="">
<label for="laps-_-lap_time">Lap time</label>
<input id="laps-_-lap_time" name="laps-_-lap_time" type="text">
<a class="remove" href="#">Remove</a>
</div>
{# Show submitted data #}
{% if data is defined %}
<p>
Received data:
{{ data }}
</p>
{% endif %}
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.