I am a developing a Ruby On Rails app using Rails 4.2.6. I am using Turbolinks alongside jquery.turbolinks (sorry I could'nt post the links to those elements as I am a newbie on the site). My problem is very simple but I just can't solve it. Here it is: I have a form fetched through AJAX
<div class="card-footer">
<a class="btn btn-sm btn-primary-outline" data-remote="true" href="/profiles/Mke5kA/positions/new"><i class="fa fa-plus"></i> Nouvelle expérience professionnelle</a>
<div id="new_position_form"></div>
</div>
The form contains Select2 elements that get their data through AJAX
= simple_form_for [profile, position], remote: true, html: {id: 'positionForm', class: 'm-b-1'} do |f|
= f.input :company_id, as: :select, input_html: {:'data-behaviour' => 'company-select2', :'data-kind' => 'company'}
= f.input :title
= f.input :summary
- location = f.object.build_location
= f.simple_fields_for :location do |l|
= render 'locations/fields', l: l, city: position.city
= render "profiles/shared/date_fields", f: f, model: position
= f.input :skill_list, as: :select, input_html: {multiple: true, :data => {:behaviour => 'acts-as-taggable', :'taggable-context' => 'skills'}}
%button.btn.btn-primary{:type => "submit"}= icon('check-square-o', 'Enregistrer')
= link_to icon('remove', 'Annuler'), 'javascript:void(0)',
data: {:'lgnk-behaviour' => "remove-form", :'lgnk-target' => "#positionForm" }, class: 'btn btn-secondary'
My problem appears when I try typing in the Select2 that are using AJAX to get the data: all the select2s are duplicated:
Here is how I get the Select2 initialized:
var loc_tag = function() {
$('[data-behaviour="acts-as-taggable"]').not('.select2-hidden-accessible').each (function (index, element) {
if ($(element).data('value')) {
var options = $(element).data('value').split(', ');
$.each(options, function(key, tag){
$(element).append($('<option selected></option>').val(tag).text(tag));
});
}
$(element).select2({
ajax: {
url: "/tags?context="+$(element).data('taggable-context'),
dataType: 'json',
headers: {
"Accept": "application/json"
},
delay: 250,
data: function (params) {
return {
q: params.term, // search term
page: params.page
};
},
processResults: function (data, page) {
return {
results: data
};
},
cache: true
},
escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
minimumInputLength: 2,
tags: true,
language: "fr",
theme: "bootstrap",
width: "100%",
placeholder: 'Mots clés...'
});
});
};
$(document).on('page:load page:update', loc_tag);
I want the Select2 elements to get initialized only once (when the form is fetched) and not upon AJAX responses on them getting their data. I have tried jQuery.not(".select2-hiden-accessible") on the elements unsing Select2 (select2-hidden-accessible being the class Select2 adds to an initialized Select2 element) but it does not work.
Many thanks for your kind help!
When using Turbolinks 5 and select2, the select2
object is no longer attached (see below for test) to the <select>
when using the back button to return to a page. A new select2
object is created and attached after going back but it was unusable.
jack's answer didn't work for me because when the new select2
object is added, the <select>
still has class='select2-hidden-accessible'
which, among other things, sets width: 1px !important
. When the new select2
object is created it's basically invisible.
The key for me was to destroy all select2 objects before TL caches the page. Here is the solution that worked for me:
$(document).on("turbolinks:before-cache", function() {
$('.select2-input').select2('destroy');
});
$(document).on('turbolinks:load', function() {
$('.select2-input').select2();
});
I believe this is the correct approach given the Turbolinks documentation (emphasis mine):
Preparing the Page to be Cached
Listen for the turbolinks:before-cache event if you need to prepare the document before Turbolinks caches it. You can use this event to reset forms, collapse expanded UI elements, or tear down any third-party widgets so the page is ready to be displayed again.
select2
ExistanceTo test if the select2
object is attached to the <select>
you can execute the following in the console:
('.select2-input').first().data('select2')
I have the same problem and I found that when you press the back button both the select
and the select2
elements are rendered but they are not bound together so when you re-initialize it with $('select).select2()
it creates another brand new select2
element next to it.
So here's what I did before initializing the select2:
If the select
is not a select2
(i.e. $(el).data('select2') == undefined
) but there is already a select2
element next to it, then remove it.
if ($(el).data('select2') == undefined && $(el).next().hasClass('select2-container')) {
$(el).next().remove();
}
$(el).select2();
2021 UPDATE:
Using the turbolinks:before-cache
:
document.addEventListener('turbolinks:before-cache', function () {
// removing the select2 from all selects
$("select").select2('destroy');
});
And loading the select2 when turbolinks:load
:
document.addEventListener("turbolinks:load", function () {
// applying select2 to all selects
$("select").select2({
theme: 'bootstrap4' // if you want to pass some custom config
});
});
Of course, you can create a custom .select2-binder
class or a data-select2-binder
to choose which select will be affected.
I had the same issue and I solved by doing:
// init.js (you can pass an container instead of using document
$( document ).on('turbolinks:load', function() {
$( document ).find('select').not('.select2-hidden-accessible').select2();
});
And now I don't have those duplicated selects :)
Nothing here worked for me.
I was able to get around this altogether by not caching the page with the select.
I use this on the top of the page with select: <% provide(:no_cache, true) %>
I use this on application.html.erb
:
<% if yield(:no_cache) %>
<meta name="turbolinks-cache-control" content="no-cache">
<% end %>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With