Search code examples
javascriptpythoncssdjangomaterialize

Django Choice Dropdown Not Working on Cloned Form in Formset


I am cloning my an empty form in django following guidance I've found elsewhere in stack overflow. The JS will append a new form as intendended however the dropdowns from a choice widget are no longer working and I can't figure out why.

I have already validated that all of my options are there using my browser's inspect feature. It appears to be that the javascript isn't working with the object.

This is from the template:


<form method="post"> {% csrf_token %}
  <div id="form_set">
  {{ FightForms.management_form|materializecss }}
  {% for form in FightForms %}
  <div class="card white darken-1 col m12">
    <table class='no_error'>
      {{ form.non_field_errors }}
      <row>
          <div class="col m3">
              <div class="fieldWrapper">
                  {{ form.guild_member_id|materializecss }}
              </div>
          </div>

    
      </row>
      {% for hidden in form.hidden_fields %}
          {{ hidden }}
      {% endfor %}
  </table>
  </div>
  {% endfor %}
</div>

  <div class="col m12">
    <div>
        <input class="btn light-blue lighten-1" type="button" value="Add More" id="add_more">
    </div>
    <br>
    <button class="btn light-blue lighten-1" type="submit">Submit Guild Members</button>
  </div>

  <div id="empty_form" style="display:none">
    <div class="card white darken-1 col m12">
      <table class='no_error'>
        {{ FightForms.empty_form.non_field_errors }}
        <row>
            <div class="col m3">
                <div class="fieldWrapper">
                    {{ FightForms.empty_form.guild_member_id|materializecss }}
                </div>
            </div>
            
        </row>
        {% for hidden in form.hidden_fields %}
            {{ hidden|materializecss }}
        {% endfor %}
      </table>
    </div>
  </div>
</form>


<script>
  $('#add_more').click(function() {
      var form_idx = $('#id_form-TOTAL_FORMS').val();
      $('#form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx));
      $('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1);
  });
</script>

Here are my header imports if it's an issue with versioning or something...

<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0"/>
    <title>League Chronicler</title>
    <link rel="shortcut icon" type="image/png" href="{% static 'img/CScroll.png' %}"/>

    <!-- CSS  -->
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <link rel="stylesheet" href="{% static 'css/chronicler.css' %}" type="text/css" media="screen,projection"/>
    <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">


    {% block css %}
      <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
      <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
    {% endblock css %}

    <!-- javascript -->
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>

</head>

EDIT/Update: So what appears to be happening is that the javascript/query function isn't copying the that the materialize wants to use as dropdown content. Instead, what is happening is that the script is appending that ul to the #empty_form. So if I hit the add form button 3 times, the empty form will have 4 ul's (original+3) and the new forms won't have any.

Walkthrough:

After clicking the button to add an empty form once I have an empty form without a ul which makes the dropdown appear empty.

enter image description here

We can see there is no ul in this new form to reference as dropdown content. enter image description here

However, if we look at the blank form, it now has 2 ul's. enter image description here

I believe the ".append()" or ".html()" is causing this.

<script>
  $('#add_more').click(function() {
      var form_idx = $('#id_form-TOTAL_FORMS').val();
      $('#form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx));
      $('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1);
  });
</script>

Solution

  • Issue:

    In your code you are initializing your choice dropdown which is inside your "#empty_form" at start so it generate all tags i.e.: <ul><li>.. etc which is not needed as that div is hidden.

    Recreating issue with dummy html:

    $('#add_more').click(function() {
      var form_idx = $('#id_form-TOTAL_FORMS').val();
      $('#form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx));
      $('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1);
    });
    
    $(document).ready(function() {
      $('select').formSelect();
    });
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
    
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
    <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
    
    
    <!-- javascript -->
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
    
    
    <div class="col m12">
      <div>
        <input class="btn light-blue lighten-1" type="button" value="Add More" id="add_more">
      </div>
      <br>
      <button class="btn light-blue lighten-1" type="submit">Submit Guild Members</button>
    </div>
    
    <input type="text" id="id_form-TOTAL_FORMS" value="1">
    
    <div id="form_set">
    
    </div>
    
    
    <div id="empty_form">
      <div class="card white darken-1 col m12">
        <select id="__prefix__">
          <option>----</option>
          <option>1</option>
          <option>2</option>
          <option>3</option>
        </select>
      </div>
    </div>


    Solution:

    Instead of initializing the choice dropdown at start keep the original dropdown as it is and once you clone the html initialize the last added choice dropdown with formSelect().

    Working Code with dummy html:

    $('#add_more').click(function() {
      var form_idx = $('#id_form-TOTAL_FORMS').val();
      $('#form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx)); //apppend it
      $('#form_set .choice_dropdown:last').formSelect(); //then initalize the last appended dropdown
      $('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1);
    });
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
    <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
    
    
    <!-- javascript -->
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
    
    
    <div class="col m12">
      <div>
        <input class="btn light-blue lighten-1" type="button" value="Add More" id="add_more">
      </div>
      <br>
      <button class="btn light-blue lighten-1" type="submit">Submit Guild Members</button>
    </div>
    
    <input type="text" id="id_form-TOTAL_FORMS" value="0">
    
    <div id="form_set">
    
    </div>
    
    
    <div id="empty_form" style="display:none;">
      <div class="card white darken-1 col m12">
        Guild Member Name:
        <!--added class to this select you can add any class name-->
        <select class="choice_dropdown" id="__prefix__">
          <option>----</option>
          <option>1</option>
          <option>2</option>
          <option>3</option>
        </select>
      </div>
    </div>