From the first screenshot below, I have a Flask app that has a search field that use Ajax request to query a list of US States to return as dropdown values. When I leave the Name
field empty and hit Submit, this is inadvertently removing the dropdown value I had earlier selected. I want to retain the dropdown field during form validation as well.
app.py
import sys
from flask import Flask, render_template, request
from forms import InputForm
app = Flask(__name__)
app.config['SECRET_KEY'] = "Development"
US_STATES = ["Alaska", "Alabama", "Arkansas", "American Samoa", "Arizona", "California",
"Colorado", "Connecticut", "District ", "of Columbia", "Delaware",
"Florida", "Georgia", "Guam", "Hawaii", "Iowa", "Idaho", "Illinois",
"Indiana", "Kansas", "Kentucky", "Louisiana", "Massachusetts", "Maryland",
"Maine", "Michigan", "Minnesota", "Missouri", "Mississippi", "Montana",
"North Carolina", "North Dakota", "Nebraska", "New Hampshire",
"New Jersey", "New Mexico", "Nevada", "New York", "Ohio", "Oklahoma",
"Oregon", "Pennsylvania", "Puerto Rico", "Rhode Island", "South Carolina",
"South Dakota", "Tennessee", "Texas", "Utah", "Virginia", "Virgin Islands",
"Vermont", "Washington", "Wisconsin", "West Virginia", "Wyoming"]
@app.route("/", methods=["GET","POST"])
def index():
form = InputForm()
if request.method == "POST":
print(form.name.data, file=sys.stderr)
print(form.states.data, file=sys.stderr)
print(form.territory.data, file=sys.stderr)
print("Hello from outside Validate.", file=sys.stderr)
if form.validate_on_submit():
print(form.name.data, file=sys.stderr)
print(form.states.data, file=sys.stderr)
print(form.territory.data, file=sys.stderr)
print("Hello from inside Validate.", file=sys.stderr)
else:
print(form.errors)
return render_template("states.html", form=form)
@app.route("/search")
def search():
text = request.args["searchText"]
result = [c for c in US_STATES if text.lower() in c.lower()]
return {"results": result}
if __name__ == '__main__':
app.run(host="localhost", port=5001, debug=True)
forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, SelectField, SubmitField
from wtforms import validators
from wtforms.validators import Optional, InputRequired, DataRequired
class InputForm(FlaskForm):
name = StringField("Name", validators=[DataRequired()])
states = SelectField("US States", validate_choice=False)
territory = SelectField("Is Territory?", choices=["Unknown", "Yes", "No"], default="Unknown", validators=[InputRequired()])
submit = SubmitField("Add State", validators=[Optional()])
templates\index.html
<!DOCTYPE html>
<html>
<head>
<title>Live Search Ajax Demo</title>
<meta charset="utf-8" />
<!-- CSS Stylesheets -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.12.1/css/bootstrap-select.css"/>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.12.1/js/bootstrap-select.js"></script>
<script type="text/javascript">
function liveSearch(value) {
value = value.trim();
if (value != "") {
$.ajax({
url: "search",
data: { searchText: value },
dataType: "json",
success: function (data) {
var res = "";
for (i in data.results) {
res += "<option value=" + data.results[i] + ">" + data.results[i] + "</option>";
}
$("#states").html(res);
},
});
} else {
$("#states").html("");
}
}
</script>
</head>
<body>
<!-- Main -->
<main role="main" class="container">
<div>
{% block content %}
{% endblock %}
</div>
</main>
</body>
</html>
templates\states.html
{% extends "index.html" %}
{% block content %}
<div class="content-section">
<div class="col-6">
<div class="row mt-4 mb-4">
<h2>Live Search in Flask with Ajax</h2>
<hr>
<div class="row mt-4 mb-4">
<form method="POST" action="" novalidate="novalidate">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<div class="row">
<div class="col">
{{ form.name.label(class="form-control-label") }}
{% if form.name.errors %}
{{ form.name(class="form-control form-control-md is-invalid") }}
<div class="invalid-feedback">
{% for error in form.name.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.name(class="form-control form-control-md") }}
{% endif %}
</div>
<div class="col">
{{ form.territory.label(class="form-control-label") }}
{% if form.territory.errors %}
{{ form.territory(class="form-control form-control-md is-invalid") }}
<div class="invalid-feedback">
{% for error in form.territory.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.territory(class="form-control form-select") }}
{% endif %}
</div>
</div>
<div class="row">
<div class="input-group mt-4 mb-4">
<input type="text" class="form-control" placeholder="Search USA States" onkeyup="liveSearch(this.value)">
{{ form.states(class="form-control form-select") }}
</input>
</div>
</div>
<div class="input-group mb-3 justify-content-md-end">
<div class="form-group">
{{ form.submit(class="btn btn-primary btn-sm") }}
</div>
</div>
</fieldset>
</form>
</div>
</div>
</div>
{% endblock content %}
The JQuery official documentation is very beginner-friendly if you need something, I suggest you to start from there.
Otherwise explain clearly what are you attempting to do.
Here is a cleaner way to do what you have already done.
Make sur that your form content has a pretty strict rules (I think you can customize this on your "form builder" in the backend) like for example :
<input name="Name" type="text" maxlength="10" required />
$(document).ready(function () {
$('#inputSearchStates').keyup(function () { // when a keyboard key is hitted (even CTRL is included and all keyboard keys...)
var value = $(this).val().trim(); // remove first and last blank spaces from your string (note that blank spaces in the middle will still remain)
if (this.form.reportValidity()) { // return true if the form is valid (required inputs already filled ...etc)
$.ajax({
url: "search",
data: {
searchText: value
},
dataType: "json",
success: function (data) {
// data is the returned result from your backend
// it can be a string/filled array/empty array/number/float ...etc
$("#states").empty(); // always make your dropdown empty (with definitely no options to select)
data.results.each(function () { // automatically fetch the response result (a JS array) if exists , else it will do nothing.
$("#states").append(`
<option value="${this}"> ${this} </option>
`); // for each available data in your response result and append <options> respectively
});
// remember that your dropdown will still empty if no data there is no data in your response array.
},
});
}
});
});