0

I have a django form with an inline formset and I'd like to give the user the ability to add/remove the fields 'approach_type' and 'approach_number'

This a snippet of the form:

    <div class="form-group">
  <div class="input-group-sm">
    <label class="text-muted">Approaches</label>
    <div class="form-control pl-4 pt-2">


        <div class="row form-row">
          <input type="hidden" name="approach_set-TOTAL_FORMS" value="1" id="id_approach_set-TOTAL_FORMS" /><input type="hidden" name="approach_set-INITIAL_FORMS" value="0" id="id_approach_set-INITIAL_FORMS" /><input type="hidden" name="approach_set-MIN_NUM_FORMS" value="0" id="id_approach_set-MIN_NUM_FORMS" /><input type="hidden" name="approach_set-MAX_NUM_FORMS" value="1000" id="id_approach_set-MAX_NUM_FORMS" />

          <input type="hidden" name="approach_set-0-id" id="id_approach_set-0-id" />
          <div class="col pr-5>"><select name="approach_set-0-approach_type" class="form-control" id="id_approach_set-0-approach_type">
  <option value="" selected>---------</option>

  <option value="ILS">ILS</option>

  <option value="CATII">CAT II</option>

  <option value="CATIII">CAT III</option>
</select></div>
          <div class="col pr-5>"><input type="number" name="approach_set-0-number" min="0" class="form-control" id="id_approach_set-0-number" /> </div>
          <div class="input-group-append">
            <button class="btn btn-success add-form-row">+</button>
          </div>
          <!-- <div class="col pr-5>"><input type="checkbox" name="approach_set-0-DELETE" class="form-control" id="id_approach_set-0-DELETE" /></div> -->
       </div>

and here is the JS from this tutorial: https://medium.com/@taranjeet/adding-forms-dynamically-to-a-django-formset-375f1090c2b0

    <script type='text/javascript'>
function updateElementIndex(el, prefix, ndx) {
    var id_regex = new RegExp('(' + prefix + '-\\d+)');
    var replacement = prefix + '-' + ndx;
    if ($(el).attr("for")) $(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
    if (el.id) el.id = el.id.replace(id_regex, replacement);
    if (el.name) el.name = el.name.replace(id_regex, replacement);
}
function cloneMore(selector, prefix) {
    var newElement = $(selector).clone(true);
    var total = $('#id_' + prefix + '-TOTAL_FORMS').val();
    newElement.find(':input').each(function() {
        var name = $(this).attr('name').replace('-' + (total-1) + '-', '-' + total + '-');
        var id = 'id_' + name;
        $(this).attr({'name': name, 'id': id}).val('').removeAttr('checked');
    });
    total++;
    $('#id_' + prefix + '-TOTAL_FORMS').val(total);
    $(selector).after(newElement);
    var conditionRow = $('.form-row:not(:last)');
    conditionRow.find('.btn.add-form-row')
    .removeClass('btn-success').addClass('btn-danger')
    .removeClass('add-form-row').addClass('remove-form-row')
    .html('<span class="glyphicon glyphicon-minus" aria-hidden="true"></span>');
    return false;
}
function deleteForm(prefix, btn) {
    var total = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());
    if (total > 1){
        btn.closest('.form-row').remove();
        var forms = $('.form-row');
        $('#id_' + prefix + '-TOTAL_FORMS').val(forms.length);
        for (var i=0, formCount=forms.length; i<formCount; i++) {
            $(forms.get(i)).find(':input').each(function() {
                updateElementIndex(this, prefix, i);
            });
        }
    }
    return false;
}
$(document).on('click', '.add-form-row', function(e){
    e.preventDefault();
    cloneMore('.form-row:last', 'form');
    return false;
});
$(document).on('click', '.remove-form-row', function(e){
    e.preventDefault();
    deleteForm('form', $(this));
    return false;
});
</script>

This line is the current problem:

var name = $(this).attr('name').replace('-' + (total-1) + '-', '-' + total + '-');

I'm sorry but I only know a very little bit of JS.

2
  • whats the actual error you get? Commented Oct 22, 2018 at 2:30
  • TypeError: $(...).attr(...) is undefined Commented Oct 22, 2018 at 2:34

1 Answer 1

1

So as you pointed out in your comment the problem is that $(this).attr('name') is returning undefined. It looks like for some reason your query is also returning the button, which if you look at the jquery documentation makes a little sense... although i dont know if I would consider a button an input automatically but they do. I'm also not 100% sure what you want this to be doing, but to solve this issue you can either wrap that chunk of code in an if block checking if the name attr is undefined or not, changing your query on the inputs to be stricter, or filter down the list you are running over. Here is a codepen that I think is working as intended, but again, didn't read the whole meium article so no promises. the relevant part is here:

 newElement.find(':input')
  .filter(function(){
    return $(this).attr('name');
})
  .each(function(){
    ...
  })

the filter function is essentially saying filter out all results where the name attribute is undefined.

let me know if this does what you're hoping for! good luck learning Javascript! I promise its more fun that what you're currently dealing with. especially if you start using ES6

Sign up to request clarification or add additional context in comments.

2 Comments

Hey man, that codepen works as intended, mostly I think. I'm going to work on this a bit and post a solution / upvote you in a bit, along with some more questions probably :) I'm confused though about why I'd want to skip undefined name attributes if the regex is designed to capture the prefix from the associated surrounding tags.
I found another solution that I was able to make work github.com/elo80ka/django-dynamic-formset/blob/master/docs/… I really appreciate the time you took to answer the question. Cheers!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.