'How to send table dropdown select fields to flask

I have a table composed of three columns, a text and two other select fields. When the user chooses an option from the dropdown select, I would like to send these data to flask.

I know I can make it work with inputs but since I am working with select, I am not sure how I can send them back to flask.

This is my table:

<div class="row" id="tablediv">
    <div class="col">
        <table class="table table-striped table-bordered" id="table"/>
    </div>
</div>


const office_cardgroups = data['cardgroups']
const office_doorgroups = data['doorgroups']

const table = document.getElementById("table");
const header = table.createTHead();
var row = header.insertRow(0);
row.insertCell(0).innerHTML = "<b>Departments</b>";
row.insertCell(1).innerHTML =  "<b>Card Groups</b>";
row.insertCell(2).innerHTML =  "<b>Door Groups</b>";

{% for department in departments %}
    var rowCount = table.rows.length;
    var row = table.insertRow(rowCount);
    row.insertCell(0).innerHTML = "{{ department.name }}";
    var row_cardgroup = '<td className="select"><select id="cardgroups_{{ department.id }}">'

    for (const [key, cardgroup] of Object.entries(office_cardgroups)) {
        var row_cardgroup = row_cardgroup + '<option value=' + cardgroup['id'] + '>'+ cardgroup['name']+'</option>'
    }
    row_cardgroup = row_cardgroup + '</select></td>'

    row.insertCell(1).innerHTML = row_cardgroup

    var row_doorgroup = '<td className="select"><select id="cardgroups_{{ department.id }}">'

    for (const [key, doorgroup] of Object.entries(office_doorgroups)) {
        var row_doorgroup = row_doorgroup + '<option value=' + doorgroup['id'] + '>'+ doorgroup['name']+'</option>'
    }

    row_doorgroup = row_doorgroup +  '</selec></td>'

    row.insertCell(2).innerHTML = row_doorgroup

{% endfor %}


Solution 1:[1]

There are few issues in the shared code:

  • table tag is not self closing so it should be
<table class="table table-striped table-bordered" id="table"></table>
  • Not sure where data['cardgroups'] and data['doorgroups'] are coming from, If they are coming from backend to jinja template then it should be wrapped with parenthesis {{}}
  • Since you are writing javascript it should be inside script tag
  • id should be unique across html right now we have 2 id with same name cardgroups_{{ department.id }} in select

Data used in example

data = {
    "departments": [{
        "id": 1,
        "name": "Dept 1",
    }, {
        "id": 2,
        "name": "Dept 2",
    }, {
        "id": 3,
        "name": "Dept 3",
    }],
    "cardgroups": [{
        "id": 4,
        "name": "Card 1",
    }, {
        "id": 5,
        "name": "Card 2",
    }, {
        "id": 6,
        "name": "Card 3",
    }],
    "doorgroups": [{
        "id": 7,
        "name": "Door 1",
    }, {
        "id": 8,
        "name": "Door 2",
    }, {
        "id": 9,
        "name": "Door 3",
    }],
}

There are 2 ways you can send data to backend

Using form

You can wrap the table with form with submit button and add name in select tag which would then make the data available in request.form

<div class="row" id="tablediv">
  <div class="col">
    <form method="POST">
      <table class="table table-striped table-bordered" id="table"></table>
      <input type="submit" />
    </form>
  </div>
</div>

<script>
    const office_cardgroups = {{ data['cardgroups'] | safe }}
    const office_doorgroups = {{ data['doorgroups'] | safe }}

    const table = document.getElementById("table");
    const header = table.createTHead();
    var row = header.insertRow(0);
    row.insertCell(0).innerHTML = "<b>Departments</b>";
    row.insertCell(1).innerHTML =  "<b>Card Groups</b>";
    row.insertCell(2).innerHTML =  "<b>Door Groups</b>";

    {% for department in data['departments'] %}
        var rowCount = table.rows.length;
        var row = table.insertRow(rowCount);
        row.insertCell(0).innerHTML = "{{ department.name }}";
        var row_cardgroup = '<td className="select"><select name="cardgroups_{{ department.id }}">'

        for (const [key, cardgroup] of Object.entries(office_cardgroups)) {
            var row_cardgroup = row_cardgroup + '<option value=' + cardgroup['id'] + '>'+ cardgroup['name']+'</option>'
        }
        row_cardgroup = row_cardgroup + '</select></td>'

        row.insertCell(1).innerHTML = row_cardgroup

        var row_doorgroup = '<td className="select"><select name="doorgroups_{{ department.id }}">'

        for (const [key, doorgroup] of Object.entries(office_doorgroups)) {
            var row_doorgroup = row_doorgroup + '<option value=' + doorgroup['id'] + '>'+ doorgroup['name']+'</option>'
        }

        row_doorgroup = row_doorgroup +  '</selec></td>'

        row.insertCell(2).innerHTML = row_doorgroup

    {% endfor %}
</script>
@app.route('/', methods=["GET", "POST"])
def index():
    print(request.form)
    return render_template('index.html', data=data)

Using javascript

<div class="row" id="tablediv">
  <div class="col">
    <table class="table table-striped table-bordered" id="table"></table>
    <button id="send-data">Submit</button>
  </div>
</div>

<script>
    const office_cardgroups = {{ data['cardgroups'] | safe }}
    const office_doorgroups = {{ data['doorgroups'] | safe }}

    const table = document.getElementById("table");
    const header = table.createTHead();
    var row = header.insertRow(0);
    row.insertCell(0).innerHTML = "<b>Departments</b>";
    row.insertCell(1).innerHTML =  "<b>Card Groups</b>";
    row.insertCell(2).innerHTML =  "<b>Door Groups</b>";

    {% for department in data['departments'] %}
        var rowCount = table.rows.length;
        var row = table.insertRow(rowCount);
        row.insertCell(0).innerHTML = "{{ department.name }}";
        var row_cardgroup = '<td className="select"><select id="cardgroups_{{ department.id }}">'

        for (const [key, cardgroup] of Object.entries(office_cardgroups)) {
            var row_cardgroup = row_cardgroup + '<option value=' + cardgroup['id'] + '>'+ cardgroup['name']+'</option>'
        }
        row_cardgroup = row_cardgroup + '</select></td>'

        row.insertCell(1).innerHTML = row_cardgroup

        var row_doorgroup = '<td className="select"><select id="doorgroups_{{ department.id }}">'

        for (const [key, doorgroup] of Object.entries(office_doorgroups)) {
            var row_doorgroup = row_doorgroup + '<option value=' + doorgroup['id'] + '>'+ doorgroup['name']+'</option>'
        }

        row_doorgroup = row_doorgroup +  '</selec></td>'

        row.insertCell(2).innerHTML = row_doorgroup

    {% endfor %}

    const departments = {{ data['departments'] | safe }}

    document.querySelector('#send-data').addEventListener('click', function() {
        const form_data = new FormData();
        departments.forEach(function(department) {
            let card_id = `cardgroups_${department.id}`;
            form_data.append(card_id, document.querySelector("#" + card_id).value);
            let door_id = `doorgroups_${department.id}`;
            form_data.append(door_id, document.querySelector("#" + door_id).value);
        })
        const url = 'http://localhost:5000/';
        const xhr = new XMLHttpRequest();

        xhr.open( 'POST', url, true );
        // xhr.setRequestHeader('Content-Type', 'multipart/form-data');
        xhr.onreadystatechange = function ( response ) {};
        xhr.send( form_data );

        // using jQuery
        // $.ajax({
        //   url: url,
        //   data: form_data,
        //   processData: false,
        //   contentType: false,
        //   type: 'POST',
        //   success: function(data){
        //     alert(data);
        //   }
        // });
    });
</script>
@app.route('/', methods=["GET", "POST"])
def index():
    print(request.form)
    return render_template('index.html', data=data)

Improvement: Template just using jinja no javascript

<div class="row" id="tablediv">
  <div class="col">
    <table class="table table-striped table-bordered" id="table">
      {% for department in data['departments'] %}
        <tr>
          <td>{{ department.name }}</td>
          <td className="select">
            <select id="cardgroups_{{ department.id }}">
              {% for card in data['cardgroups'] %}
                  <option value='{{ card.id }}'>{{ card.name }}</option>
              {% endfor %}
            </select>
          </td>
          <td className="select">
            <select id="doorgroups_{{ department.id }}">
              {% for door in data['doorgroups'] %}
                  <option value='{{ door.id }}'>{{ door.name }}</option>
              {% endfor %}
            </select>
          </td>
        </tr>
      {% endfor %}
    </table>
  </div>
</div>

Note: If you want to send on each select value separately you can addEventListener to each select.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1