使用 ajax 的 Select2 被 Rails turbolinks 事件初始化了几次

Posted

技术标签:

【中文标题】使用 ajax 的 Select2 被 Rails turbolinks 事件初始化了几次【英文标题】:Select2 with ajax gets initialized several times with Rails turbolinks events 【发布时间】:2016-07-29 14:33:15 【问题描述】:

我正在使用 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 元素正常(正确激活):

当我尝试输入使用 AJAX 获取数据的 Select2 时出现我的问题:所有 select2 都重复:

这是我如何初始化 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-hiden-accessible") (select2-hidden-accessible 是 Select2 添加到初始化的 Select2 元素的类),但它不起作用。

非常感谢您的帮助!

【问题讨论】:

【参考方案1】:

使用 Turbolinks 5 和 select2 时,使用返回按钮返回页面时,select2 对象不再附加(请参阅下面的测试)到 &lt;select&gt;。返回后创建并附加了一个新的select2 对象,但它无法使用。

jack 的回答对我不起作用,因为当添加新的 select2 对象时,&lt;select&gt; 仍然具有 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 documentation(强调我的),我相信这是正确的方法:

准备要缓存的页面

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

测试select2 存在

要测试select2 对象是否附加到&lt;select&gt;,您可以在控制台中执行以下操作:

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

【讨论】:

为我工作!谢谢。它比杰克的更优雅 您可以使用以下命令销毁所有 select2 选择:document.addEventListener('turbolinks:before-cache', function() $('.select2-hidden-accessible').select2('destroy'); ); @Pioz 解决方案避免了额外的检查步骤,效果很好。 @Pioz 您的解决方案有效,但需要添加 if($('.select2-input').first().data('select2')) 条件以检查仅当 select2 附加到该输入时才销毁【参考方案2】:

我有同样的问题,我发现当你按下后退按钮时,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();

【讨论】:

我觉得真的应该是addressed upstream。【参考方案3】:

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 来选择将影响哪个选择。


我遇到了同样的问题,我通过这样做解决了:

// init.js (you can pass an container instead of using document

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

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

【讨论】:

这确实不会导致重复选择,但是当使用“后退”按钮返回带有 select2 框的页面时,无法再选择选择框。 @jack's 不太优雅,但这里的解决方案更合适。【参考方案4】:

这里没有什么对我有用。

通过不使用选择缓存页面,我能够完全解决这个问题。

我在页面顶部使用这个选择:&lt;% provide(:no_cache, true) %&gt;

我在application.html.erb上使用这个:

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

【讨论】:

【参考方案5】:

它对我有用。即使单击后退、前进或单击其他链接并返回到初始化 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
)

更新

我看到了一些很好的建议here 你可以选择其中之一。我选择移动标签中的所有 javascript 而不是将它们放在 body 标签中。

【讨论】:

以上是关于使用 ajax 的 Select2 被 Rails turbolinks 事件初始化了几次的主要内容,如果未能解决你的问题,请参考以下文章

示例 - Ruby on Rails 6/7 的 Select2 多个 AJAX 代码(没有 Gem)

select2 rails 下拉菜单无法正确呈现

使用 AJAX 预填充 Select2 - “未找到结果”

带有 select2 的 Rails 5 ask_as_taggable

使用 Ajax 源设置 select2 元素初始值的简单示例?

Rails 5 和 select2 错误,select2 不是函数