Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine different field types in flask-wtf wtforms

I am building a form using flask-wtf forms. I have a field where users can select multiple options. If the choice they want isn't in my list, I would like to allow users to select "other" and specify their option via a textfield.

I have found no way to do this in wtforms. I have googled around and cannot seem to find an answer anywhere. My scenario here is not at all unique, and I believe it would be quite common. So I think I could be thinking/going about it wrong.

question1_options = [('rice','rice'),('chips','chips'),('tuna','tuna'), ('other', 'other')]
question2_options = [('yes', 'yes'),('no', 'no')]

class MyForm(FlaskForm):
    q1 = SelectMultipleField('favourite food?', choices=question1_options)
    q2 = RadioField('do you like football', choices=question2_options)

What I want to achieve:

Combine SelectMultipleField with a StringField so that if the appropriate options are not in the list, users can select "other" and input what they would like.

like image 900
RooneyMUFC Avatar asked Oct 15 '25 08:10

RooneyMUFC


1 Answers

I couldn't find method to combine two fields.

I think it should be two separated fields visible all time

q1_options = [('rice','rice'),('chips','chips'),('tuna','tuna')] #, ('other', 'other')]

class MyForm(FlaskForm):
    q1 = SelectMultipleField('Favourite food?', choices=q1_options)
    q1_other = StringField('Other favourite food?')

or it should use JavaScript to show StringField when you select other.

I tried to create minimal working code.

Because it uses multiple select so it needs more complex JavaScript code.

from flask import Flask, render_template_string
from flask_wtf import FlaskForm
from wtforms import SelectMultipleField, StringField, RadioField
from wtforms.validators import DataRequired, Optional

# --- form ---

q1_options = [('rice','rice'),('chips','chips'),('tuna','tuna'), ('other', 'other')]

class MyForm(FlaskForm):
    q1_list  = SelectMultipleField('Favourite food?', choices=q1_options, validators=[DataRequired()])
    q1_other = StringField('Other', validators=[Optional()])

# --- app ---

app = Flask(__name__)
app.config['SECRET_KEY'] = 'qwerty123456'

@app.route('/', methods=['GET', 'POST'])
def index():
    form = MyForm()
    #print(dir(form))
    print(form.validate_on_submit())
    #if form.validate_on_submit():
    if form.is_submitted():
        print('list:', form.q1_list.data)
        print('other:', form.q1_other.data)

    return render_template_string('''<!DOCTYPE html>    
<html>
<head>
<meta charset="utf-8">
</head>
<body>    

<form method="POST">
    {{ form.q1_list.label }}<br>
        {{ form.q1_list(onchange="get_selection(this);") }}<br>
        {{ form.q1_other(style="display:none") }}<br>
    <input type="submit" value="Go">
</form>

<script language="javascript">
//var q1_list  = document.querySelector("#q1_list");
var q1_other = document.querySelector("#q1_other");

function get_selection(select){
  //console.log(select);
  //console.log(select.value);
  //console.log(select.options);

  var opts = [];
  var opt;    
  
  var len = select.options.length;
  
  for(var i = 0; i < len; i++) {
    opt = select.options[i];

    if (opt.selected) {
      opts.push(opt.value);
    }
  }    
  
  //console.log(opts);
  
  if(opts.includes('other')){
    q1_other.style.display = 'block';
  }else{
    q1_other.style.display = 'none';
  }
}
</script>

</body>
</html>
''', form=form)

# --- start ---

if __name__ == '__main__':
    app.debug = True
    app.run()

The same with addEventListener instead of onchange

<form method="POST">
    {{ form.q1_list.label }}<br>
        {{ form.q1_list() }}<br>
        {{ form.q1_other(style="display:none") }}<br>
    <input type="submit" value="Go">
</form>

<script language="javascript">
//var q1_list  = document.querySelector("#q1_list");
var q1_other = document.querySelector("#q1_other");

q1_list.addEventListener('input', function(event) {
  //console.log(event);
  //console.log(event.target.value);
  //console.log(event.target.options);

  var select = event.target;
  
  //console.log(select.options);

  var opts=[];
  var opt;    
  
  var len = select.options.length;
  
  for(var i = 0; i < len; i++) {
    opt = select.options[i];

    if (opt.selected) {
      opts.push(opt.value);
    }
  }    
  
  //console.log(opts);
  
  if(opts.includes('other')){
    q1_other.style.display = 'block';
  }else{
    q1_other.style.display = 'none';
  }
}, false);
</script>
like image 132
furas Avatar answered Oct 16 '25 21:10

furas



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!