使用Rails turbolinks事件时,Select2与ajax被初始化多次。

23

我正在开发一个使用Rails 4.2.6的Ruby On Rails应用程序。我同时使用了Turbolinks和jquery.turbolinks(抱歉,我作为新手无法在网站上发布这些元素的链接)。我的问题非常简单,但我就是解决不了。它在这里:
我有一个通过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>

该表单包含通过 AJAX 获取数据的 Select2 元素。

= 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'
  • 目前,Select2元素是在Rails Trubolinks事件"page:load page:update"上“激活”的,但我也尝试过“page:change”
  • 当表单被获取时:select2元素很好(正确激活):

Initial form (after AJAX fetch

当我尝试在使用AJAX获取数据的Select2中输入时,出现了问题:所有的Select2都被复制了:

enter image description here

以下是初始化Select2的方法:

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);

我希望Select2元素仅在表单获取时初始化,而不是在它们获取数据的AJAX响应上进行初始化。我尝试在使用Select2的元素上使用jQuery.not(“ .select2-hidden-accessible”)(select2-hidden-accessible是Select2添加到已初始化Select2元素的类),但它不起作用。

非常感谢您的帮助!

6个回答

42

当使用Turbolinks 5和select2时,当使用后退按钮返回页面时,select2对象不再附加到<select>上(请参见下面的测试)。在返回后会创建并附加一个新的select2对象,但它是无法使用的。

jack的答案对我没有用,因为当添加新的select2对象时,<select>仍然有class='select2-hidden-accessible',其中包括设置了width: 1px !important等内容。因此,新的select2对象基本上是看不见的。

关键是在TL缓存页面之前销毁所有select2对象。以下是对我有效的解决方案:

$(document).on("turbolinks:before-cache", function() {
  $('.select2-input').select2('destroy');
});

$(document).on('turbolinks:load', function() {
  $('.select2-input').select2();
});

更多详细信息

我相信这是正确的方法,根据 Turbolinks 文档(重点在于我):

准备页面以进行缓存

如果您需要在 Turbolinks 缓存页面之前准备文档,则可以监听 turbolinks:before-cache 事件。您可以使用此事件来重置表单、折叠已展开的 UI 元素或撤消任何第三方小部件,以便页面准备好再次显示。

测试 select2 的存在

要测试 select2 对象是否附加到 <select> 上,可以在控制台中执行以下操作:

('.select2-input').first().data('select2')

1
可以了,谢谢。而且比Jack的更优雅。 - Yasser Sobhy
10
您可以使用以下代码销毁所有的 select2 选择框:document.addEventListener('turbolinks:before-cache', function() { $('.select2-hidden-accessible').select2('destroy'); }); - Pioz
@Pioz的解决方案避免了额外的检查步骤,运行良好。 - abhishek77in
@Pioz,你的解决方案有效,但需要添加if($('.select2-input').first().data('select2'))条件来检查只有在该输入框附加了select2时才销毁。 - Amit Patel

6

2021年更新:

使用turbolinks:before-cache

document.addEventListener('turbolinks:before-cache', function () {
  // removing the select2 from all selects
  $("select").select2('destroy');
});

turbolinks:load 事件中加载 select2:

document.addEventListener("turbolinks:load", function () {
  // applying select2 to all selects
  $("select").select2({
    theme: 'bootstrap4' // if you want to pass some custom config
  });
});

当然,您可以创建一个自定义的 `.select2-binder` 类或 `data-select2-binder` 属性来选择要影响的 select 元素。
我曾经遇到了同样的问题,我通过以下方式解决:
// init.js (you can pass an container instead of using document

$( document ).on('turbolinks:load', function() {
  $( document ).find('select').not('.select2-hidden-accessible').select2();
});

现在我没有那些重复的选择了 :)


1
这确实不会导致重复选择,但是当使用“返回”按钮返回到一个带有select2框的页面时,selectbox将无法再被选择。@jack的解决方案可能不太优雅,但在这里更为适当。 - edwardmp

5
我有同样的问题,我发现当你按下后退按钮时,selectselect2元素都被渲染出来,但它们没有绑在一起,因此当你用$('select).select2()重新初始化时,它会在旁边创建一个全新的select2元素。
所以,在初始化select2之前,我做了以下操作:
如果select不是select2(即$(el).data('select2') == undefined),但旁边已经有一个select2元素,则将其删除。
if ($(el).data('select2') == undefined && $(el).next().hasClass('select2-container')) {
  $(el).next().remove();
}
$(el).select2();

我觉得这个问题应该在上游解决(https://github.com/argerim/select2-rails/issues/47)。 - mlt

2

针对Rails 7和Select2.js用户的注意事项。

在Rails 7版本中,Turbo-links已被弃用。新库的名称是Turbo。 https://turbo.hotwired.dev/handbook/drive 您可以将下面的标签添加到HTML的head部分以防止缓存。这样,Select2.js就可以正常工作了。 文档

<meta name="turbo-visit-control" content="reload">

1

对我来说,这里没有任何有用的东西。

我通过不缓存包含选择器的页面来完全避免了这个问题。

我在带有选择器的页面顶部使用以下代码:<% provide(:no_cache, true) %>

我在application.html.erb中使用以下内容:

<% if yield(:no_cache) %>
  <meta name="turbolinks-cache-control" content="no-cache">
<% end %>

0

对我来说它有效。即使点击后退、前进或单击其他链接并返回到初始化select2的页面,也不会出现重复。

document.addEventListener("turbolinks:load", function() {
  $('.select2-container').remove() // this will remove all the select2 containers
  $('.select2-input').select2(); // and this will reinit the select2 again
})

更新

我看到了一些好的建议在这里,你可以选择其中之一。我选择将所有的JavaScript代码移动到标签中,而不是放在标签中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接