jQuery form repeater and select2 dont work together

15.1k views Asked by At

I am using Select2 and jQuery form repeater (https://github.com/DubFriend/jquery.repeater)

I have searched on google/so for 2 days, but cant seem to get it to work.

include jquery/select2.js/jquery.repeater.js

var form = $('#form');
form.find('select').select2();

form.repeater({
    show: function () {
    $(this).show(function(){
        form.find('select').select2('destroy').select2();
    });
    },
    hide: function (remove) {
      $(this).hide(remove);
    }
});

The problem is the jQuery.repeater clones the div tag in which the input and select elements are when select2 is already initialized and has already changed the DOM, so jQuery.repeater copies the changed DOM. I tried to destroy select2 before the repeat action is called, but that dindt work either.

15

There are 15 answers

4
2SHAE On BEST ANSWER

I'm working on a project where I use jQuery.repeater to repeat multiple select2 inputs. Following approach helped to solve my problem of initializing the inputs after they are loaded.

$('.job_repeater').repeater({
  show: function () {
    $(this).slideDown();
    $('.select2-container').remove();
    $('Replace with your select identifier id,class,etc.').select2({
      placeholder: "Placeholder text",
      allowClear: true
    });
    $('.select2-container').css('width','100%');
  },
  hide: function (remove) {
    if(confirm('Confirm Question')) {
      $(this).slideUp(remove);
    }
  }
});

More exact jQuery selectors will help to only remove/initialize the selects you want to.

The CSS line I use always after initializing the select2 to adjust the width to the parent div/container.

Best regards

0
Bindrid On

try this

include jquery/select2.js/jquery.repeater.js

var form = $('#form');
form.find('select').select2();

form.repeater({
    show: function () {
    $(this).show(function(){
        // you have not really created this second one
        // so the destroy does not work.
        // Since it is just a copy of the html,
        form.find('select').next('.select2-container').remove();
        form.find('select').select2();
    });
    },
    hide: function (remove) {
      $(this).hide(remove);
    }
});
0
RYP On

Hope this is not too late, this is working on my end. Had to manually 'destroy' select2 and remove all attached attributes to all select elements so as not to confuse the script and return the element in its 'original' state.

$('.repeater').repeater({
    show: function (){
        $(this).slideDown(function(){
            var selects = $('body').find('.pm-select2');
            $.each(selects, function(i, selectElement){
                $(selectElement).removeClass('select2-hidden-accessible').next('.select2-container').remove();
                $(selectElement).removeAttr('data-select2-id tabindex aria-hidden');
                initSelect2(selectElement);
            });
        });
    },
    hide: function (){
        $(this).slideUp();
    },
    isFirstItemUndeletable: true
});

function initSelect2(selectElement) {
    $(selectElement).select2({
        minimumResultsForSearch: Infinity
    });
}
0
Seán McCabe On

If anyone else stumbles across this, I had the same problem in getting it to work and the solution is destroying the select2 and recreating.

I then had the issue that the select2 won't sit in the correct position when opened. For instance doing a repeat that results in a page scroll causes any select2 after the page scroll to then open in the position of the first.

To fix this (have the select2 open where it should (down the page)), set the dropdownParent to its immediate parent as so:

currentSelect2.select2({
    placeholder: "Select...",
    width: '100%', 
    allowClear: true,
    dropdownParent: $(this).parent()
});
0
Sapnesh Naik On

Here is a solution which initializes select2 on form repeater button clock.

<script type="text/javascript">

 

    $("p").mouseover(function(){

        setTimeout(function(){

 

            $(".select2").select2({

                placeholder: "Select a state",

                allowClear: true

            }); 

                  

        }, 100);

    }); 

 

</script>

For an in depth overview of the problem and solution see here

0
Jahidul Islam On

Please try this one. first, select the repeater button event.

var form = $('#form');
form.find('select').select2();
    
$('#repeater-button').click(function (){
 form.repeater();
 form.find('select').select2();
})
0
Nelson On

I solved it by first initializing the original select2 on document ready and then targeting with $this each instance of the repeated selected2 element on the repeater show method. For instance, if you have a simple select dropdown and a multiple select your code should look like this:

$(document).ready(function () {
  $(".select2").select2({
       placeholder: "Select a state",
       allowClear: true
    });
});

$('.repeater').repeater({

        show: function () {
            $(this).slideDown();
            $(this).find('.select2-multiple').select2({

                placeholder: "Select Title",

                allowClear: true,

            });
            $(this).find('.select2').select2({

                placeholder: "Select Title",

                allowClear: true,

            });


        },
        hide: function (deleteElement) {
            if (confirm('Are you sure you want to delete this element?')) {
                $(this).slideUp(deleteElement);
            }
        }
    });
1
Nuhel On

The main thing is select2 is being initialized before repeater is initialized,

As repeater change the name of all type of input fields so after repeater getting initialized select2 couldn't find the correct reference , so its make a mess.

So we need to initialize select2 once our repeater is ready. Here is the code below:

 $(document).ready(function () {
        $('.repeater').repeater({
            initEmpty: false, // it also could be true
            show: function () {
    $(this).slideDown(function(){
     $(this).find('.select2').select2({
      placeholder: 'Select an option'
     });
    });
            },
            
            hide: function (deleteElement) {
                $(this).slideUp(deleteElement);
            },
            
            ready: function (setIndexes) {
               $('.select2').select2({
     placeholder: 'Select an option'
    });
            },
            isFirstItemUndeletable: false
        })
    });

1
Shivesh Tripathi On

so i had same problem and solved it with help of "2SHAE" answer but if you use another select2 somewhere in page outside the repeater it gets removed as well , by using: $(this).find(".select2-container").remove();
You can prevent that so code should look like this

$(".invoice-repeater, .repeater-default").repeater({
        show: function() {
            $(this).slideDown(),

                $(this).find(".select2-container").remove();
            $('your identifier').select2({
                placeholder: "placeholder text",
                allowClear: true
            });
            $('.select2-container').css('width', '100%');

        },
        hide: function(e) {
            // snb('error', 'Data deleted', 'Deleted');
            $(this).slideUp(e);
        },
    });
0
Packiyaraj Patchath On

If ID is not unique select2 may not work, so remove that and then Remove select2 container which is copied from previous element. Finally Then reinitiate select2

$('.repeater').repeater({
    isFirstItemUndeletable: true,
    initEmpty: false,
    show: function () {
        $(this).slideDown();
        $('.select2').removeAttr("id").removeAttr("data-select2-id");
        $(this).find('.select2-container').remove();
        $(this).find('.select2').select2()
    },
    hide: function (removeEle) {
        if(confirm('Sure?')) {
            $(this).slideUp(removeEle);
        }
    },
});
0
Daniel Pages Chacon On

just adding my two cents here with the solution that worked for me.

1- I added an id to the button that creates the new element in the repeater: id="repeater-button"

<form id="attributes_repeater">
    <!--begin::Form group-->
    <div class="form-group">
        <div data-repeater-list="attributes_repeater">
            <div class="form-group row">
                <div class="col-md-10 px-0 ps-3">
                    <select name="attribute" class="form-select select2-attribute fs-base fw-normal py-1" data-control="select2">
                        <option></option>
                        <option value="1">Option1</option>
                        <option value="2">Option2</option>
                    </select>
                </div>
                <div class="col-md-2 px-0 px-1">
                    <a href="javascript:;" data-repeater-delete class="btn btn-sm btn-light-danger mt-1 py-1 px-2">
                        <i class="la la-trash-o"></i>
                    </a>
                </div>
            </div>
        </div>
    </div>
    <!--end::Form group-->

    <!--begin::Form group-->
    <div class="form-group mt-5">
        <a href="javascript:;" data-repeater-create class="btn btn-light-primary" id="repeater-button">
            <i class="la la-plus"></i>Add
        </a>
    </div>
    <!--end::Form group-->
</form>

2- Then I added the initialization of the select2 component within the ready method of the repeater.Just to make sure that the first component is initialized the first time.

$('#attributes_repeater').repeater({
    initEmpty: false,
    isFirstItemUndeletable: true,

    ready: function () {
        $( document ).ready(function() {
            $(".select2-attribute").select2({
                placeholder: "Select..."
            });
        });
    },

    show: function () {
        $(this).slideDown();
    },

    hide: function (deleteElement) {
        $(this).slideUp(deleteElement);
    }
});

3- Then we create the onClick method for the Add button of the repeater (the one for which we created the id in the first step). This will ensure that we initialize all the new created elements. Notice that we are waiting 100 ms. It is just to make sure that the new component is already created when we execute the script.

$("#repeater-button").click(function(){
    setTimeout(function(){
        $(".select2-attribute").select2({
            placeholder: "Select..."
        });
    }, 100);
});

0
Daniyal Khan On

I might be too late for this answer. If you have only one select field in your repeater. Just try adding this one line.

$(this).find('select').select2();

Worked for me.

2
VIVEK On
form.repeater({
  show: function() {
    $(this).slideDown(); 
    $(this).find('select').each(function() {
      if (typeof $(this).attr('id') === "undefined") {
        // ...
      } else {
        $('.select2').removeAttr("id").removeAttr("data-select2-id"); //some times id was not unique So select2 not working, so i remove id 
        $('.select2').select2();
        $('.select2-container').css('width','100%');
        $('.select2').next().next().remove();
      }
    });
  }
});
0
Ageng D. Prastyawan On

All above example doesn't work if you have any select2 fields outside repeater inside your form. because the remove script is select from forms, not the new created instance. try this

var form = $('#form');
form.find('select').select2();

form.repeater({
    show: function () {
        $(this).show(function(){
            $(this).find('.select2-container').remove(); // bind $(this) so if you have more select2 field outside repeater then it doesnt removed
            form.select2();
        });
    },
    hide: function (remove) {
      $(this).hide(remove);
    }
});
0
Rob On

When using the initEmpty: false option, initialising select2 at the show function doesn't work for the first row. In this case you can also initialise select2 when the ready function runs.

$('.repeater').repeater({
    isFirstItemUndeletable: false,
    initEmpty: false,
    ready: function () {
        $('.select2').select2({
            placeholder: "Select an option...",
        });
    },
    show: function () {
        $(this).slideDown();
        $('.select2').select2({
            placeholder: "Select an option...",
        });
    },
    hide: function () {
        $(this).slideUp();
    },
});