Reputation: 13
I am trying to setup a dynamic form in Flask where the form has a dynamically populated FieldList of FormFields. While most of it works, I keep running into issues with the variables of each FormField. I need each FormField to have a label which is the name of the FormField (and is rendered on page) and a custom ID which is used in a show/hide script which uses a dynamically filled SelectField to select a sub-form to show and hide. However, I cannot get the variables to retain their state after the form is submitted, which also prevents me from correctly processing the returned data.
These are my form classes:
class ColumnForm(Form):
text = Label('', '')
condition = SelectField('Condition', choices=[('eq', 'Equal to'), ('contains', 'Contains')], validators=[Optional()])
value = StringField(validators=[Optional()])
class CategoryForm(Form):
catName = ''
Columns = FieldList(FormField(ColumnForm))
def add_field(self, name):
# I have tried several ways of adding the formFields to the FieldList, this is the only way I get the variables to be renamed, but I do not have a way to get the submitted data back
if not self.checkIfformInFieldList(name):
self.Columns.append_entry()#{'text' : name})
self.Columns[-1].form.text = Label(len(self.Columns)-1, name)
self.Columns[-1].name = name
self.Columns[-1].id = name
self.Columns[-1].form.condition.id = f'{name}-condition'
self.Columns[-1].form.condition.name = f'{name}-condition'
self.Columns[-1].form.value.name = name+'-value'
self.Columns[-1].form.value.id = name+'-value'
self.Columns[-1].form.value.validators = [Optional()]
def checkIfformInFieldList(self, name)->bool:
for column in self.Columns:
if column.name == name:
return True
return False
class baseForm(FlaskForm):
table_select = SelectField('Select Table', choices=['Select Table'],validators=[Optional()])
submit = SubmitField('Filter')
tables = FieldList(FormField(CategoryForm))
def add_category(self, catName:str, columnNames:list):
self.tables.append_entry()
self.tables[-1].name = catName
self.tables[-1].id = catName
for columnName in columnNames:
self.tables[-1].add_field(columnName)
Here is (a simplified version of) my form initialization:
@app.route('/', methods=['GET', 'POST']))
def index():
form = baseForm(request.form)
databaseStructure = get_database_structure(db_dict)
for table in databaseStructure:
form.table_select.choices.append(table)
if not form.tables.entries:
for table, columns in databaseStructure.items():
form.add_category(table, columns)
if request.method == 'POST' and form.validate():
print(form.data)
return render_template('index.html', form = form)
Here is (a simplified version) of my html template:
<form method="post">
{{ form.hidden_tag() }}
<p>
{{ form.table_select.label }}<br>
{{ form.table_select }}
</p>
{% for category in form.tables %}
<div id = "{{ category.name }}">
{% for column in category.Columns %}
<p>
{{ column.form.text }}
{{ column.form.condition }}
{{ column.form.value }}
</p>{% endfor %}
</div>
{% endfor %}
<p>{{ form.submit() }}</p>
</form>
<script>
document.addEventListener('DOMContentLoaded', function () {
const tableSelect = document.querySelector('#table_select');
const columnSelect = document.querySelector('#column_select');
tableSelect.addEventListener('change', function () {
localStorage.setItem('selectedTable', tableSelect.value)
});
});
window.onload = showFormFunction()
document.getElementById("table_select").onchange = function() {showFormFunction()};
function showFormFunction() {
let table_select_value = document.getElementById("table_select").value;
{% for tableName in form.tables %}{% if loop.index0 == 0 %}
if (table_select_value == "{{ tableName.id }}"){ {% else %} else if (table_select_value == "{{ tableName.id }}"){ {% endif %}
document.getElementById("{{ tableName.id }}").style.display = "inline"; {% for tableNamex in form.tables %} {% if tableName.id != tableNamex.id %}
document.getElementById("{{ tableNamex.id }}").style.display = "none"; {% endif %} {% endfor %}
}{% endfor %} else { {% for tableName in form.tables %}
document.getElementById("{{ tableName.id }}").style.display = "none";{% endfor %}
}
};
</script>
Upvotes: 1
Views: 23