Динамическое добавление / удаление Flask полей и автозаполнение с javascript - PullRequest
0 голосов
/ 20 января 2020

Я пытаюсь достичь следующего одновременно в Flask:

  1. Возможность динамически добавлять и удалять строки (в данном случае просто StringField) в таблице wtform
  2. Возможность автозаполнения в этом StringField

Я реализовал следующие сценарии на основе поиска, который я сделал в Интернете. По сути, я собрал два javascripts, найденных из двух разных онлайн-источников.

Проблема, с которой я столкнулся, заключается в том, что кажется, что функция «Добавить / удалить строку» работает хорошо, а автозаполнение работает хорошо для предварительно заполненных строк (то есть строк, созданных из Flask перед рендерингом страницы). Однако автозаполнение не выполняется для строк, созданных кнопкой «Добавить».

Чтобы показать эффект, первый пи c ниже показывает автозаполнение для предварительно заполненного поля и работает нормально. enter image description here

Второй пи c ниже показывает проблему с автозаполнением в поле, созданном кнопкой '+ Добавить фрукты':

Я набрал буквы «Или», и появляется раскрывающееся меню, но после выбора (щелчка) «Оранжевый» в раскрывающемся списке поле не заполняется (поэтому в текстовом поле остается «Или»), а затем курсор переходит на строку выше . Желаемым поведением должно быть поле, заполненное «Orange» после нажатия, а курсор остается в текущей строке.

enter image description here

Я потратил на это часы и не мог понять, что не так с моим кодом. Я действительно ценю, если кто-то может помочь в этом. Ниже приведены два сценария для репликации проблемы:

Python код:

from flask import Flask, render_template, jsonify
from flask_wtf import FlaskForm
from wtforms import StringField, FieldList, FormField, SubmitField
from wtforms.validators import DataRequired 


app = Flask(__name__)

class FruitForm(FlaskForm):
    name = StringField('Fruit', validators=[DataRequired('Name')])

class BasketForm(FlaskForm):
    fruits = FieldList(FormField(FruitForm))
    submit = SubmitField('Submit')

@app.route('/autocomplete')
def autocomplete():
    choices = ['Apple', 'Orange', 'Banana']
    return jsonify(choices)

@app.route('/', methods=['GET', 'POST'])
def home():   
    form = BasketForm()
    form.fruits = [
        FruitForm(name="Apple")
    ]

    if form.validate_on_submit():
        pass
    return render_template('home.html', form=form)


if __name__ == '__main__':
    import os
    app.config['SECRET_KEY'] = os.urandom(32)
    app.run(debug=True, port=5002)

html / javascript код

<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- jQuery -->
    <script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script>
    <script src="https://code.jquery.com/ui/1.12.0/jquery-ui.min.js" integrity="sha256-eGE6blurk5sHj+rmkfsGYeKyZx3M4bG+ZlFyA7Kns7E=" crossorigin="anonymous"></script> 

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
  </head>

<body>

  <main role="main" class="container">

    <form method="POST" class="form-group">
        {{ form.hidden_tag() }}

        <div data-toggle="fieldset" id="basket-fieldset">
      <!-- Add Button -->
          <div class="row">
            <div class="col">
              <button class="btn btn-info" type="button" data-toggle="fieldset-add-row" data-target="#basket-fieldset">+ Add Fruit</button>
            </div>
          </div>

      <!-- Fruit List -->
          {% for fruit in form.fruits %}
          <div class="row" data-toggle="fieldset-entry">
            <div class="col">{{ fruit.name(class="form-control autocomplete") }}</div>
            <div class="col"><button class="btn btn-danger" type="button" data-toggle="fieldset-remove-row" id="port-fruit-{{loop.index0}}-remove">-</button></div>
          </div>
          {% endfor %}

        </div>

    </form>


    <script>
      $(function() {
      /////////////////////////////////////////////////////////////////////////////////////
      // Dynamically Add / Remove a row
      $("div[data-toggle=fieldset]").each(function() {
              var $this = $(this);

              //Add new entry
              $this.find("button[data-toggle=fieldset-add-row]").click(function() {
          var target = $($(this).data("target"))
          console.log(target);
          var oldrow = target.find("[data-toggle=fieldset-entry]:last");
          var row = oldrow.clone(true, true);
          console.log(row.find(":input")[0]);
          var elem_id = row.find(":input")[0].id;
          var elem_num = parseInt(elem_id.replace(/.*-(\d{1,4})-.*/m, '$1')) + 1;
          row.find(":input").each(function() {
                      console.log(this);
                      var id = $(this).attr('id').replace('-' + (elem_num - 1) + '-', '-' + (elem_num) + '-');
                      $(this).attr('name', id).attr('id', id).val('');
          });
          oldrow.after(row);
              }); //End add new entry

              //Remove row
              $this.find("button[data-toggle=fieldset-remove-row]").click(function() {
          if($this.find("[data-toggle=fieldset-entry]").length > 1) {
                      var thisRow = $(this).closest("[data-toggle=fieldset-entry]");
                      thisRow.remove();
          }
              }); //End remove row
      });


      /////////////////////////////////////////////////////////////////////////////////////
      // Autocomplete Fruit name
      $(".autocomplete").autocomplete({
          source:function(request, response) {
          $.getJSON("{{url_for('autocomplete')}}",{
              q: request.term, // in flask, "q" will be the argument to look for using request.args
          }, function(data) {
              response(data); // matching_results from jsonify
          });
          },
          minLength: 2,
          select: function(event, ui) {
          console.log(ui.item.value); 
          }
      });


      });

    </script>

  </main>
</body>
</html>

вы можете запустить приложение, просто поместив два скрипта в одну папку, а затем выполните python3 app.py, чтобы поиграть с приложением. Спасибо

...