0

In my HTML form, it's possible to add additional inputs dynamically by clicking a button. I've got this part to work, however I need each input to have a unique name.

This is my code so far,

<div class="control-group field_wrapper">
  <label class="control-label"><strong> Phone Number 1</strong></label>
  <input type="text" class="input-medium" name="phone_number[]">
  <button class="btn btn-success add-number" type="button" title="Add">Add</button>
</div>
<div class="additionalNumber"></div>

My JS as below,

$(document).ready(function(){
  var maxField = 10; 
  var addButton = $('.add-number'); 
  var wrapper = $('.additionalNumber'); 
  function fieldHTML(inputNumber) {
      return `<div class="control-group field_wrapper">\
                <label class="control-label"><strong> Phone Number ${inputNumber}</strong></label>\
                <input type="text" class="input-medium"  name="phone_number[${inputNumber}]">\
                <button class="btn btn-danger remove" type="button">Remove</button>\
              </div>`;
  }
  var x = 1;
  $(addButton).on('click', function(e) {
    if (x < maxField) {
      x++;
      $(wrapper).append(fieldHTML(x));
    }
    if (x >= maxField) {
      alert('Limited to 10.');
    }
  });
  $(wrapper).on('click', '.remove', function(e) {
    e.preventDefault();
    $(this).parents('.control-group').remove();
    x--; 
  });
});

Using this code, I can get unique name for each input which are created by dynamically. But my problem is name[x] index not works properly when it is removing. That mean, just think I have added 3 input and delete second one and again I am adding new one, then it has same name twice. In this case, it is phone_number[3] for second input and phone_number[3] for thirt one also.

This is the fiddle from above code. Any help is appreciated.

4
  • It's likely relevant: What is the back-end that you're POSTing these names to? If it's asp.net-mvc then you don't want them to be indexed, ie just name="phone_number[]" and not name="phone_number[1]" Commented Sep 7, 2022 at 16:21
  • The usual solution is, on remove, just hide the elements so that they keep the index then you don't get overlapping index values and there are no "gaps" in the indices. Add a "name=removed" field and set it on remove. Then in your back-end, ignore rows that are removed. Commented Sep 7, 2022 at 16:23
  • @freedomn-m, Here I am using php as my back-end and in my case it is need to be indexed. According to your 2nd comment, I tried it, but it is also word as remove. Commented Sep 7, 2022 at 16:55
  • "but it is also word as remove" not sure what that means. If you mean remove is a reserved word, then use something else, eg "virtuallydeleted" or "flagasremoved" (add _ / Pascal case as required) Commented Sep 7, 2022 at 17:01

1 Answer 1

1

You don't need to index the inputs for PHP either - 3x inputs named phone_number[] will automatically be indexed 0 - 2 on the back end:

<input type="text" name="phone_number[]">
<input type="text" name="phone_number[]">
<input type="text" name="phone_number[]">

[phone_number] => Array
    (
        [0] => a
        [1] => b
        [2] => c
    )

That doesn't help with your plain text Phone Number n label though. And maybe you have your own reasons to want an input name index.

If you think about it, if you're going to allow deletions of any item in the list, and you need the results to be sequential, the only option is to renumber everything each time you make a change. You don't need to include any numbering when you add a new element, just regenerate all numbering.

Here's a working snippet doing that. My changes:

  • No need to pass the count x to fieldHTML(), we're going to renumber everything after you add the element;

  • Add a <span class='count'></span> in your label, which we can target and update;

  • Add a reNumber() function which will iterate over all inputs on the page and number them sequentially;

  • Call that function after any change;

Notes:

  • The 2 separate tests if (x < maxField) and if (x >= maxField) can be combined into a single if/else;

  • If you want to get rid of the duplication of your HTML block, you could give the first one an id like template, and then instead of duplicating that HTML in your JS, just copy the template, eg :

    let $copy = $('#template').clone();
    wrapper.append($copy);
    
  • wrapper and addButton are already jQuery objects, no need to wrap them with $() a second time to use them;

  • If you do want to number your input names, for consistency the first should probably be phone_number[1];

$(document).ready(function() {
    var x = 1;
    var maxField = 10;
    var addButton = $('.add-number');
    var wrapper = $('.additionalNumber');

    function fieldHTML() {
        return `<div class="control-group field_wrapper">\
                <label class="control-label"><strong> Phone Number <span class='count'></span></strong></label>\
                <input type="text" class="input-medium" name="phone_number[]">\
                <button class="btn btn-danger remove" type="button">Remove</button>\
              </div>`;
    }

    /**
     * Iterate over all inputs and renumber sequentially
     */
    function reNumber() {
        let count;
        wrapper.find('.field_wrapper').each(function (i) {
            // .each() index is 0-based, and input #1 is already on the page,
            // so extras start at #2
            count = i + 2;
            $('.count', $(this)).html(count);

            // If you want to index your input names, but you can safely leave
            // this out, PHP will index them anyway
            $('input', $(this)).attr('name', 'phone_number[' + count + ']')
        });
    }

    addButton.on('click', function(e) {
        if (x < maxField) {
            x++;
            wrapper.append(fieldHTML());
            reNumber();
        } else {
            alert('Limited to 10.');
        }
    });

    wrapper.on('click', '.remove', function(e) {
        e.preventDefault();
        $(this).parents('.control-group').remove();
        x--;
        reNumber();
    });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="control-group field_wrapper">
  <label class="control-label"><strong> Phone Number 1</strong></label>
  <input type="text" class="input-medium" name="phone_number[]">
  <button class="btn btn-success add-number" type="button" title="Add">Add</button>
</div>
<div class="additionalNumber"></div>

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.