Fill in the WTForms field using the value selected from the previous field

New to this, trying to create an application, following the well-known Flask tutorial, using Flask-bootstrap, Flask-wtforms, Jinja, etc.

I have a form with two selection fields and a button.

class Form(FlaskForm): school_year = SelectField('School year', choices=some_tuples_list) category = SelectField('Category', choices=[]) submit = SubmitField('submit') 

I want only the first field to be pre-populated, and the other to fill (on the client side?) Based on the previously selected field value.

In the template, I will try something like

 {{ form.school_year(**{"onchange":"getCategories()"}) }} 

which works fine (assuming I return a list of tuples to populate the next field using the correct javascript and route), but I need something like the following

 {{ wtf.form_field(form.school_year(**{"onchange":"getCategories()"})) }} 

which does not work (Error: wtforms.widgets.core.HTMLString object 'has no attributes' flags')

So, I think my question is actually: how can I implement the onChange event in this wtf form field? (And is that what I should do, or is there a way from the view function?)

Thanks in advance.

+9
source share
3 answers

Here is an example implementation of this logic for working with the built-in functions of WTForms. The trick here is, if you want to use WTForms validation, you need to instantiate the form with any possible value, and then change the available options in Javascript to display the filtered values ​​based on another choice.

In this example, I'm going to use the concept of states and counties (I work with a lot of geodata, so this is the general implementation I'm building).

Here is my form, I have assigned unique identifiers to important elements to access them from Javascript:

 class PickCounty(Form): form_name = HiddenField('Form Name') state = SelectField('State:', validators=[DataRequired()], id='select_state') county = SelectField('County:', validators=[DataRequired()], id='select_county') submit = SubmitField('Select County!') 

Now in the "Checkbox" view for creating and processing the form:

 @app.route('/pick_county/', methods=['GET', 'POST']) def pick_county(): form = PickCounty(form_name='PickCounty') form.state.choices = [(row.ID, row.Name) for row in State.query.all()] form.county.choices = [(row.ID, row.Name) for row in County.query.all()] if request.method == 'GET': return render_template('pick_county.html', form=form) if form.validate_on_submit() and request.form['form_name'] == 'PickCounty': # code to process form flash('state: %s, county: %s' % (form.state.data, form.county.data)) return redirect(url_for('pick_county')) 

Kind of flask for answering XHR requests for counties:

 @app.route('/_get_counties/') def _get_counties(): state = request.args.get('state', '01', type=str) counties = [(row.ID, row.Name) for row in County.query.filter_by(state=state).all()] return jsonify(counties) 

And finally, javascript to place at the bottom of your Jinja template. I assume you mentioned Bootstrap that you are using jQuery. I also assume this is in the javascript line, so I use Jinja to return the correct URL for the endpoint.

 <script charset="utf-8" type="text/javascript"> $(function() { // jQuery selection for the 2 select boxes var dropdown = { state: $('#select_state'), county: $('#select_county') }; // call to update on load updateCounties(); // function to call XHR and update county dropdown function updateCounties() { var send = { state: dropdown.state.val() }; dropdown.county.attr('disabled', 'disabled'); dropdown.county.empty(); $.getJSON("{{ url_for('_get_counties') }}", send, function(data) { data.forEach(function(item) { dropdown.county.append( $('<option>', { value: item[0], text: item[1] }) ); }); dropdown.county.removeAttr('disabled'); }); } // event listener to state dropdown change dropdown.state.on('change', function() { updateCounties(); }); }); </script> 
+21
source

PJ Santoro's answer is great. A download update was called, but the event listener did not work for me at first. It turned out that I did not change the β€œstate” for my own field identifier, since I thought it was a keyword related to the field state! D'o! Therefore, looking for other options, I found that this also worked, which might be useful to someone there:

  // event listener to state dropdown change $('#state').change(function() { updateCounties(); }); 
0
source

The answer abigperson nailed it for me .... in the end.

The only thing I need to add that would save me many hours of screaming on the screen is that the current Bootstrap code snippets from their site use a thin version of jQuery. The thin version does not contain some functions that abigperson uses in its code.

The slide in the full version of jQuery fixed things for me right away.

A little further reading suggests that Bootstrap will still work fine with the full version of jQuery, so the game is on.

0
source

Source: https://habr.com/ru/post/1261533/


All Articles