简体   繁体   中英

Display wtforms SelectMultipleField display as drop-down and not list

I am using wtforms SelectMultipleField to render a form with a list of choices.

Using a plain vanilla SelectField renders the choices as a drop-down ( dropdown ).

However, I need to be able to select multiple entries in the list, hence want to use SelectMultipleField. This, however, displays as a listbox ( listbox ).

This is my code for the form entries: test = SelectMultipleField('test', choices=[(1, 'a'), (2, 'b'), (3, 'c')])

Code to render: <a class="test" id="test">{{ form.test }}</a>

How can I render my SelectMultipleField to display as a dropdown?

(I'm still looking for more 'vanilla' solutions, this one depends on some JQuery library (maybe this library exists because there are no vanilla ways))

link to that library

main.py:

from flask import Flask, render_template, request
from flask_wtf import FlaskForm
from wtforms.fields import SelectMultipleField, SubmitField

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


choices = [
    'apple',
    'banana',
    'carrot',
]


class Form(FlaskForm):
    global choices
    select_multiple_field = SelectMultipleField(choices=choices)
    submit = SubmitField()


@app.route('/', methods=['GET', 'POST'])
def index():
    form = Form()

    if request.method == 'POST':
        # Getting selected options
        form_data = form.select_multiple_field.data
        print(form_data)

    return render_template(
        'index.html',
        form=form,
    )


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

index.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <link href="https://unpkg.com/multiple-select@1.5.2/dist/multiple-select.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script>
    <script src="https://unpkg.com/multiple-select@1.5.2/dist/multiple-select.min.js"></script>
</head>

<body>

    <style>
        select {
            width: 20%;
        }
    </style>

    <form action="{{ url_for('index') }}" method="POST">
        {{ form.hidden_tag() }}
        {{ form.select_multiple_field(**{"multiple": "multiple"}) }}
        {{ form.submit }}
    </form>

    <script>
        $(function () {
            $('select').multipleSelect({
                multiple: true,
                multipleWidth: 60
            })
        })
    </script>

</body>

</html>

(feel free to contact me if this answer was not exactly what you are looking for and tell why so, I'll try to adjust it:D)

There is a solution but it is completely custom that doesn't depend on how wtf forms display the field, that's why it probably doesn't behave like normal wtf forms input. It is useful to customize each part in the field and how to display it

1- Create a custom macro to display the field [create a file named macros.html and place it inside the templates folder]

macros.html

{% macro multi_bootstrap_select_field(field, class="", options_classes="") %}
   {% if field %}
     {{ field.label }}: <select {% if field.id  %}id="{{field.id}}"{% endif %} {% if field.name  %}name="{{field.name}}"{% endif %} {% if class  %} class="{{class}}"{% endif %} {% if field.placeholder  %} placeholder="{{field.placeholder}}"{% endif %} {% if field.flags.required %}"required=required"{%endif%} {% if field.type|lower == 'selectmultiplefield' or field.type|lower == 'multicheckboxfield' %}multiple="multiple"{% endif %}>
       {% if field.choices %}
         {% for choice_index in range(field.choices|length)  %}
           {% if field.choices[choice_index]|length == 2 %}
             <option {% if field.name %}name="{{field.name}}"{% endif %} value="{{field.choices[choice_index][0]}}">{{field.choices[choice_index][1]}}</option>
           {% endif %}
         {% endfor %}
       {% endif %}
     </select>
     {% for error in field.errors %}
       <div class="alert alert-danger" role="alert">{{ error }}</div>
     {% endfor %}
   {% endif %}
{% endmacro %}

2- Import "macros.html" into the page containing the field

index.html

{% from 'macros.html' multi_bootstrap_select_field %}
 
{{ multi_bootstrap_select_field(form.nums, class="form-control") }}

forms.py

from flask_wtf import FlaskForm
from wtforms import SubmitField, SelectMultipleField, widgets
from wtforms.validators import InputRequired

class MultiCheckboxField(SelectMultipleField):
    widget = widgets.ListWidget(prefix_label=False)
    option_widget = widgets.CheckboxInput()



class addLyrics(FlaskForm):
    nums = MultiCheckboxField('Tags',
                           coerce=str,
                           choices=[(1, 'one'), (2, 'two'), (3, 'three')],
                           validators=[InputRequired()])
    submit = SubmitField("Create")

If you are going to pass additional arguments, you need to define them in your custom macro function

for example

{% macro multi_bootstrap_select_field(field, class="", options_classes="", placeholder="") %}

{{ multi_bootstrap_select_field(form.nums, class="form-control", placeholder="something") }}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM