2

This code gives add-more functionality with jquery clone & Append methods for select2 input which is not responding after clicking add more. What am I missing?

    <div class="row">

      <div class="col-md-12 service-group">
            <div class="row">
                <div class="form-group mb-3 col-md-6">
                    <label class="form-label">Service</label>
                    <div >
                        <select type="text" class="form-select service-select" placeholder="Services" value="" name="items[0][service]" id="service-0" required>
                            <option value="" disabled selected>Select your option</option>
                            @foreach ($services as $service)
                                <option value="{{$service->service_name}}"  data-id="{{$service->amount}}">{{$service->service_name}}</option>
                            @endforeach
                            
                            
                        </select>
                        
                    </div>
                    </div>

                    <div class="form-group mb-3 col-md-6">
                    <label class="form-label">Amount</label>
                    <div >
                        <input type="text" class="form-control" name="items[0][amount]" id="amount-0" placeholder="Amount"  required>
                  
                    </div>
                </div>
                <div class="form-group mb-3 col-md-12">
                  <label class="form-label">Description</label>
                  <textarea class="form-control" id="description-0" name="items[0][description]" rows="6" placeholder="Description.." required></textarea>
              </div>
            </div>

      </div>

      
    </div>

</div>

<div class="more-service-box"></div>

<div class="col-md-12">
    <button type="button" id="addmore" class="btn btn-default"><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-circle-plus" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
      <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
      <circle cx="12" cy="12" r="9"></circle>
      <line x1="9" y1="12" x2="15" y2="12"></line>
      <line x1="12" y1="9" x2="12" y2="15"></line>
  </svg> Add More</button>
    <button type="button"   id="remove" class="btn btn-default"><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-circle-minus" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
      <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
      <circle cx="12" cy="12" r="9"></circle>
      <line x1="9" y1="12" x2="15" y2="12"></line>
  </svg> Remove</button>
</div>
  $(".service-select").select2({
    placeholder: "Select a programming language",
    allowClear: true
  });


// Add Service , Amount, Description
$('#addmore').click(function () {

    var index = $('.service-group').length;
    
    var $service = $('.service-group:first').clone();

    $service.find('select[name*=service]')
        .val('')
        .attr('name', 'items[' + index + '][service]')
        .attr('id', 'service-' + index);

    $service.find('input[name*=amount]')
        .val('')
        .attr('name', 'items[' + index + '][amount]')
        .attr('id', 'amount-' + index);

    $service.find('textarea[name*=description]')
        .val('')
        .attr('name', 'items[' + index + '][description]')
        .attr('id', 'description-' + index);

        
        

    $service.appendTo('.more-service-box');


    $("body").on("change", "select[id=service-"+ index +"]", function() {
        //get amount for data-attribute
        var amount = $(this).find("option:selected").data("id");
        //assign value
        $(this).closest(".service-group").find("input[id=amount-"+ index +"]").val(amount)
    })


   
    
});



$("body").on("change", "select[id=service-0]", function() {
    //get amount for data-attribute
    var amount = $(this).find("option:selected").data("id");
    //assign value
    $(this).closest(".service-group").find("input[id=amount-0]").val(amount)
})





$("#remove").on("click", function() {  
    $(".more-service-box").children().last().remove();  
});  
devsam247
  • 1,280
  • 13
  • 18
  • 1
    I've converted your code to a snippet and added the required css/js from the [tag:jquery-select2] [SO wiki](https://stackoverflow.com/tags/jquery-select2/info). This adds select2 4.0.4 though it shouldn't matter. **Your code works fine as provided.** Please update the snippet with the relevant script includes etc to *demonstrate* the issue. – freedomn-m Sep 29 '22 at 17:03
  • 1
    It worked when I use the same version of Jquery and select2 as seen here. But now my problem is different because I created an add more button for new select input which does not respond after the addition of the select input plugin. Can I just update the code here for you to see? – devsam247 Sep 30 '22 at 10:27
  • jQuery only works on elements that exist at the time the code runs - so `$(".service-select").select2({` applies to the .service-selects that exist at that time. If you *add* another one dynamically later, it won't automatically become a select2. You need to initialise select2 after you add your new element to the DOM, ie after `$service.appendTo` – freedomn-m Sep 30 '22 at 13:20
  • Conversely, if you're using event delegation `$("body").on("change",` then set that up for all elements in one go and you don't need to add it after appending new ones; that's what event delegation is for. You'll need to make a small change for the `.find(input + index` but having a class that's shared across all of them - as long as there's only one per .service-group. – freedomn-m Sep 30 '22 at 13:21
  • @freedomn-m can you share a working example, please? – devsam247 Sep 30 '22 at 13:33

1 Answers1

1

You need to initialise any new selects after they are added to the DOM. The initial $(".service-select").select2( will only apply to the ones that exist at the time.

In your code, the first step would be to copy the initialisation code to after $service.appendTo:

$service.appendTo('.more-service-box');
$('#service-' + index).select2({
    placeholder: "Select a programming language",
    allowClear: true
});

However, this will only work if you create a new select (without the ).

Normally you would clone a <template> which wouldn't be initialised to a , but as you're cloning .service-group:first there's an added complication: if you clone() a parent, it clones the [tag:select2] elements so when you re-init it with .select2() it doesn't work (as the elements are already there).

The ideal solution would be to use a <template> (as that's what it's for).

In your case, you need to remove the before cloning, then add it back after cloning.

$('.service-group:first').find("select").select2("destroy")

Updated snippet:

$(".service-select").select2({
  placeholder: "Select a programming language",
  allowClear: true
});

$('#addmore').click(function() {
  var index = $('.service-group').length;
  $('.service-group:first').find("select").select2("destroy")
  var $service = $('.service-group:first').clone();
  
  $service.find('select[name*=service]')
    .val('')
    .attr('name', 'items[' + index + '][service]')
    .attr('id', 'service-' + index);

  $service.appendTo('.more-service-box');

  $('.service-group').find("select").select2({
    placeholder: "Select a programming language",
    allowClear: true
  });
});

$("#remove").on("click", function() {
  $(".more-service-box").children().last().remove();
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.4/css/select2.min.css" rel="stylesheet" />
<script src="//code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.4/js/select2.min.js"></script>

<div class="row">
  <div class="col-md-12 service-group">
    <div class="row">
      <div class="form-group mb-3 col-md-6">
        <label class="form-label">Service</label>
        <div>
          <select type="text" class="form-select service-select" placeholder="Services" value="" name="items[0][service]" required>
            <option value="" disabled selected>Select your option</option>
            <option value="service 1">service 1</option>
            <option value="service 2">service 2</option>
            <option value="service 3">service 3</option>
          </select>
        </div>
      </div>
    </div>
  </div>
</div>

<div class="more-service-box"></div>

<div class="col-md-12">
  <button type="button" id="addmore" class="btn btn-default">Add More</button>
  <button type="button" id="remove" class="btn btn-default">Remove</button>
</div>
freedomn-m
  • 27,664
  • 8
  • 35
  • 57
  • Using both lines of code does not work. However, the first line of code gets the appended element working but it does not work on the initial element because it only selects the ids of the appended elements. – devsam247 Sep 30 '22 at 14:33