简体   繁体   中英

Can I make dependent select box populated through Jinja variables?

I'm using Flask with Jinja2 templates, how can I make dependent select pox using Jinja2 or using Java script + Jinja2 variable?

Example:

Python side:

@app.route('/')
def dependent():
    cars= {'Chevrolet':['Volt','Malibu','Camry'],'Toyota':['Yaris','Corolla'],'KIA':['Cerato','Rio']}
    return render_template('select.html',cars=cars)

HTML+Jinja side

<form>
<select name="car_vendor" id="car_vendor">
    {% for key, value in cars.items() %}
        <option value="{{ key }}"> {{ key }} </option>
    {% endfor %}
</select>


<select name="car_model" id="car_model">
    ---
    ---
</select>
</form>

How can I populate car_model select box dependently on the car_vendor select box like in the picture:

PS: I'm completely ignorant regarding JS, jQuery and Ajax, I've seen many JS or jQuery examples and I was like a man lost and paralyzed in the desert, So please if you're going to help, edit my code example and don't link me to external links. Thanks in advance.

在此处输入图片说明

First, you need to specify which car vendor and model have been selected to determine how the dependent select box should be populated:

cars = {'Chevrolet':['Volt','Malibu',Camry],'Toyota':['Yaris','Corolla'],'KIA':['Cerato','Rio']}
return render_template('select.html',cars=cars, selected_car='Toyota', selected_model = 'Prius')

Second, for the first select box you should just be enumerating keys rather than items since you have no use for the dictionary's values. What you are doing "works" but is less clear. You should be setting the selected property of the option that has been selected:

<select name="car_vendor" id="car_vendor">
    {% for key in cars.keys() %}
        <option value="{{ key }}" {% if key == selected_car %}selected="selected"{% endif %}> {{ key }} </option>
    {% endfor %}
</select>

Finally, the car model that has been selected determines which set of models to populate the dependent select box with:

<select name="car_model" id="car_model">
    {% for model in cars[selected_car] %}
        <option value="{{ model}}" {% if model == selected_model %}selected="selected"{% endif %}> {{ model}} </option>
    {% endfor %}
</select>
<form>

Now the above assumes there is always a car and car model selected. You might start out with no selections where selected_car and selected_model are both None . To support this, the first select box should have an initial tag:

<option value=''>*** Select a car make ***</option>

This will be selected by default if no other option as the selected attribute set. (You can and should add a similar option to the second select box.) And since selected_car is None , you need to surround the second select box with a test for this:

{% if selected_car %}
    <select name="car_model" id="car_model">
    etc.
{% endif %}

And so the second text box will not be shown until there is a selection in the first box.

But you must always make sure that when there is a selection change in the first box, that the form is submitted and the new selection can be reflected in the second box. So you need to add to the first select box:

<form name="f">
    <select name="car_vendor" id="car_vendor" onchange="document.f.submit();">

Update: Sample Flow

  1. When the server-side script is first invoked neither form variable car_vendor nor car_model is set. Consequently the template is invoked with both template variables selected_car and selected_model set to None . This results in the car vendor select box being populated with no selection and the car model select box not being displayed.
  2. The user selects a Toyota from the car vendor select box. It should be arranged that whenever a selection is made from this box that if the car model box is being displayed, then any car model selection is first cleared before the form is submitted.
  3. The form is submitted to the server-side script with form variable car_vendor set to 'Toyota' and car_model not set and so template variables selected_car is set to Toyota and selected_model being set to None . This results in the car model select box displaying Toyota as the currently selected car and the car model select box being displayed with all the Toyota models but with no current selection.
  4. The user now selects Chevrolet in the car vendor select box. Because the car model select box is being displayed any selection it has (it currently has none) should be first cleared before the form is submitted.
  5. The form is submitted to the server-side script with form variable car_vendor set to 'Chevrolet' and car_model not set and so template variables selected_car is set to Chevrolet and selected_model being set to None . This results in the car model select box displaying Chevrolet as the currently selected car and the car model select box being displayed with all the Chevrolet models but with no current selection.
  6. The user selects Camry from the car model select box.
  7. The form is submitted to the server-side script with form variable car_vendor set to 'Chevrolet' and car_model set to 'Camry' and so template variables selected_car is set to Chevrolet and selected_model id set to Camry . This results in the car model select box displaying Chevrolet as the currently selected car and the car model select box being displayed with all the Chevrolet models and 'Camry' as the current selection. In addition, whatever processing and output is performed now that both a vendor and model have been selected. Note that now if a new vendor is selected, the current model will first be de-selected before the form is submitted to ensure that a model that belongs to a different vendor is not sent up with the form. This is the reason for clearing the car model selection when a new car vendor is selected.

Sample Code Based on Sample Flow (more or less)

from flask import Flask, request, render_template_string
app = Flask(__name__)


@app.route('/', methods=['POST', 'GET'])
def dependent():
    cars = {'Chevrolet':['Volt','Malibu','Camry'],'Toyota':['Yaris','Corolla'],'KIA':['Cerato','Rio']}
    # look for data from form POST (won't be present when we are invoked with GET initially)
    selected_car = request.form['car_vendor'] if 'car_vendor' in request.form else None
    if not selected_car or selected_car not in cars:
        selected_car = None
        selected_model = None
    else:
        selected_model = request.form['car_model'] if 'car_model' in request.form else None
        # This is an alternative to unselecting the car model whenever a new car vendor is selected.
        # Instead, we just check whether the selected model belongs to the selected car vendor or not:
        if selected_model and selected_model not in cars[selected_car]:
            selected_model = None
    # Use a string template for demonstration purposes:
    return render_template_string("""
<!DOCTYPE html>
<html>
<head>
<title>Flask Demo</title>
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, minimum-scale=1.0">
</head>
<body>
<h2>Select Car</h2>
<form name="f" method="post">
  <table>
    <tr>
      <th>Make</th>
    <th>Model</th>
    </tr>
    <tr>
      <td>
        <select name="car_vendor" id="car_vendor" onchange="document.f.submit();">
{% if not selected_car %}
          <option value=''>-- Select a Make --</option>
{% endif %}
{% for car in cars.keys() %}
          <option value="{{ car }}" {% if car == selected_car %}selected="selected"{% endif %}>{{ car }}</option>
{% endfor %}
        </select>
      </td>
      <td>
{% if selected_car %}
        <select name="car_model" id="car_model" onchange="document.f.submit();">
  {% if not selected_model %}
          <option value=''>-- Select a Model --</option>
  {% endif %}
  {% for model in cars[selected_car] %}
          <option value="{{ model }}" {% if model == selected_model %}selected="selected"{% endif %}>{{ model }}</option>
  {% endfor %}
        </select>
{% endif %}
      </td>
    </tr>
  </table>
</form>

{% if selected_car and selected_model %}
<p style="color: red;">Both make and model have been selected: {{ selected_car }} and {{ selected_model }}.</p>
{% endif %}
</body>
</html>
""", cars=cars, selected_car=selected_car, selected_model=selected_model)

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