如何正确地将 flask-wtforms 与 AJAX 集成以动态添加字段?

问题描述 投票:0回答:2

我是ajax编程的初学者,所以请多多包涵。

我想要做的是允许用户在单击按钮时添加一个表单字段,如下所示:

Before button click

After button click

对于主窗体,我有以下代码。

class EnrollForm(FlaskForm):
  known_for = StringField('Known for',
    validators=[
      Length(max=LONG_STRING)
    ]
  )

  soc_med_info = FieldList(FormField(SocialMediaForm), min_entries=1)

这是它的子表单/表单域

class SocialMediaForm(Form):
  social_media = SelectField('Where can we find you?', coerce=str)

  other = StringField(validators=[
    Length(max=MEDIUM_STRING)
  ])

  handle = StringField('Handle',
    validators=[
      Length(max=SHORT_STRING)
    ]
  )

  followers = StringField('Followers',
    validators=[
      Length(max=SHORT_STRING),
      Regexp('^\d*[kmKM]$', message='Input number of followers')
    ]
  )

html (enroll.html)

<div class="row">
    <div class="col-lg-12">
        <form method="POST"
        action="{{url_for('user.enroll')}}"
        id="enroll-form">
          {{ form.hidden_tag() }}
            <fieldset class="form-group">
              <legend>
                  Be one of us
              </legend>
              <div class="form-group">
              {{ form.known_for.label(class="form-control-label") }}

              {% if form.known_for.errors %}
                {{ form.known_for(class="form-control form-control-sm is-invalid")}}
                <div class="invalid-feedback">
                  {% for error in form.known_for.errors %}
                      <span>{{ error }}</span>
                  {% endfor %}
                </div>
              {% else %}
                {{ form.known_for(class="form-control form-control-sm")}}
              {%endif%}
            </div>
            <fieldset>
                <legend>
                    Social Media Information
                </legend>
                <span id="platforms">
                  {% include 'pages/user/platforms.html' %}
                </span>
            </fieldset>
          </fieldset>
        </form>
    </div>
</div>

注意包含的 html 文件(“platforms.html”)——这是我呈现子表单字段行的地方

{% for soc_med in form.soc_med_info %}
  <div class="row">
      <div class="col-md-4">
          <div class="form-group">
            {{ soc_med.social_media.label(class="form-control-label") }}

            {% if soc_med.social_media.errors %}
              {{ soc_med.social_media(class="form-control form-control-sm is-invalid")}}
              <div class="invalid-feedback">
                {% for error in form.social_media.errors %}
                    <span>{{ error }}</span>
                {% endfor %}
              </div>
            {% else %}
              {{ soc_med.social_media(class="form-control form-control-sm")}}
            {%endif%}
          </div>
      </div>
      <div class="col-md-4">
          <div class="form-group">
            {{ soc_med.handle.label(class="form-control-label") }}

            {% if soc_med.handle.errors %}
              {{ soc_med.handle(class="form-control form-control-sm is-invalid")}}
              <div class="invalid-feedback">
                {% for error in form.handle.errors %}
                    <span>{{ error }}</span>
                {% endfor %}
              </div>
            {% else %}
              {{ soc_med.handle(class="form-control form-control-sm")}}
            {%endif%}
          </div>
      </div>
      <div class="col-md-2">
          <div class="form-group">
            {{ soc_med.followers.label(class="form-control-label") }}

            {% if soc_med.followers.errors %}
              {{ soc_med.followers(class="form-control form-control-sm is-invalid")}}
              <div class="invalid-feedback">
                {% for error in form.followers.errors %}
                    <span>{{ error }}</span>
                {% endfor %}
              </div>
            {% else %}
              {{ soc_med.followers(class="form-control form-control-sm")}}
            {%endif%}
          </div>
      </div>
      <div class="col-md-2">
          <div class="form-group">
            {% if loop.index == 1 %}
              <button type="button"
              id="add-soc-med-btn">
                  Add Item
              </button>
            {% else %}
              <button type="button">
                  X
              </button>
            {% endif %}
          </div>
      </div>
  </div>
{% endfor %}

AJAX 脚本:

<script type="text/javascript">
    $("#add-soc-med-btn").on("click", function(evt){
        evt.preventDefault();
        $.ajax({
          url: "{{url_for('user.add_soc_med')}}",
          type: "POST",
          data: $("#enroll-form").serialize(),
          success: function(data) {
            console.log(data)
            $('#platforms').html(data)
          },
          error: function(xhr, resp, text){
            console.log(xhr, resp, text)
          }
        });
    });
</script>

“观点”:

@user.route('/enroll', methods=['GET', 'POST'])
@login_required
def enroll():
  form = EnrollForm()

  # Initialize form
  for soc_form in form.soc_med_info:
    soc_form.social_media.choices = soc_med_platforms

  if form.validate_on_submit():
    return doEnroll(request, form)

  return render_template('pages/user/enroll.html', form=form)

def doEnroll(request, form):
  pass

@user.route('/enroll/add/social_media', methods=['POST'])
@login_required
def add_soc_med():
  form = EnrollForm(request.form)
  form.soc_med_info.append_entry()
  for soc_form in form.soc_med_info:
    soc_form.social_media.choices = soc_med_platforms

  return render_template('pages/user/platforms.html', form=form)

“删除行”功能尚未实现。 预期的结果是连续添加字段行,但是重复点击按钮也只添加了一个字段行

我假设这是因为在视图代码中,“add_soc_med()”;我正在重新初始化 EnrollForm。我想要做的是保留“enroll()”函数中的表单并访问 soc_med_info 字段列表并附加一个条目。

我只是不知道该怎么做。

我在此线程上使用 Jelle van der Zwaag 的回答: 使用 AJAX 使用 Flask-WTForms 将条目附加到 FieldList

javascript python ajax flask wtforms
2个回答
0
投票

你是正确的,在 add_soc_med 视图函数中重新初始化 EnrollForm 是导致问题的原因。表单的状态不会在请求之间保留,因此您需要一种不同的方法。一种解决方案是使用 JavaScript 克隆现有的表单字段并将其附加到表单而不向服务器发出请求。

这是您的 JavaScript 代码的更新版本:

$("#add-soc-med-btn").on("click", function (evt) {
    evt.preventDefault();
    // Clone the last row in the form
    let newRow = $("#platforms .row").last().clone();

    // Increment the indices in the field names and ids to ensure unique names
    newRow.find(":input").each(function () {
        let nameAttr = $(this).attr("name");
        let idAttr = $(this).attr("id");
        let indexPattern = /\d+/;
        let index = parseInt(nameAttr.match(indexPattern)[0]);

        let newIndex = index + 1;
        let newNameAttr = nameAttr.replace(indexPattern, newIndex);
        let newIdAttr = idAttr.replace(indexPattern, newIndex);

        $(this).attr("name", newNameAttr);
        $(this).attr("id", newIdAttr);
    });

    // Clear the input values of the new row
    newRow.find(":input").val('');

    // Update the button in the new row to the "X" button
    newRow.find(".col-md-2 button").first().text('X');

    // Append the new row to the form
    $("#platforms").append(newRow);
});

现在,使用这段 JavaScript 代码,表单将在重复单击按钮时不断添加新行,而无需向服务器发出请求。这也有助于减少服务器负载,因为表单操作完全在客户端完成。然后您可以删除 add_soc_med 视图函数,因为它不再需要了。

请记住,此解决方案采用特定的表单结构,并且字段名称和 ID 遵循示例中使用的模式。如果您的表单结构发生变化,请务必调整代码。


0
投票

基本上你的方法有效。
但是,单击该按钮后,您将替换整个表单,包括用于添加子表单的按钮。这也意味着不再分配点击事件的监听器。要添加更多条目,需要在填充模板后再次注册监听器。
一个可能的解决方案如下。

function handleClick(evt) {
        evt.preventDefault();
        $.ajax({
            url: "{{url_for('user.add_soc_med')}}",
            type: "POST",
            data: $("#enroll-form").serialize(),
            success: function(data) {
                $("#add-soc-med-btn", $('#platforms').html(data))
                    .on("click", handleClick);
            },
            error: function(xhr, resp, text){
                console.log(xhr, resp, text)
            }
        });
}

$("#add-soc-med-btn").on("click", handleClick);
© www.soinside.com 2019 - 2024. All rights reserved.