LoginSignup
2
2

More than 5 years have passed since last update.

rails-bootstrap-formsでmultiple: trueを使った時にinputのidがlabelのforと一致しない問題

Last updated at Posted at 2016-05-26

発端

rails-bootstrap-formsを使っていて、何故かラベルをクリックしてもチェックボックスの操作ができない!となったので、原因を調査してみました。

原因

ラベルのfor属性と、チェックボックスのidが一致していなかった。
問題のerbがこちら。

<%= bootstrap_form_tag method: :get, url: search_path, layout: :horizontal do |f| %>
  <%= f.check_box :target_types, { multiple: true, include_hidden: false, checked: params[:target_types].blank? || params[:target_types].include?("Test::Alpha") }, "Test::Alpha" %>
  <%= f.check_box :target_types, { multiple: true, include_hidden: false, checked: params[:target_types].blank? || params[:target_types].include?("Test::Beta") }, "Test::Beta" %>
  <%= f.submit "Search", class: 'btn btn-primary' %>
<% end %>

生成されるhtml(問題の部分のみ抽出)

<div class="checkbox">
  <label for="_target_types_Test::Alpha">
    <input checked="checked" id="_target_types_testalpha" name="[target_types][]" type="checkbox" value="Test::Alpha"> Target types
  </label>
</div>

labelのfor属性が"_target_types_Test::Alpha"になっているが、inputのidが"_target_types_testalpha"となっている。

より詳しい原因を究明するために、rails-bootstrap-formsの実装を追っていく。

def check_box_with_bootstrap(name, options = {}, checked_value = "1", unchecked_value = "0", &block)
  options = options.symbolize_keys!

  html = check_box_without_bootstrap(name, options.except(:label, :help, :inline), checked_value, unchecked_value)
  label_content = block_given? ? capture(&block) : options[:label]
  html.concat(" ").concat(label_content || (object && object.class.human_attribute_name(name)) || name.to_s.humanize)

  label_name = name
  label_name = "#{name}_#{checked_value}" if options[:multiple]

  if options[:inline]
    label(label_name, html, class: "checkbox-inline")
  else
    content_tag(:div, class: "checkbox") do
      label(label_name, html)
    end
  end
end

ここらへんにbinding.pryを仕掛けて処理を追っていく。

まずラベルの生成はlabel(label_name, html)で、ActionView::Helpers::FormBuilder#labelに飛ばしている。
multiple: trueにした場合、
label_name = "#{name}_#{checked_value}" if options[:multiple]で、ここでは"_target_types_Test::Alpha"が入っている。

def label(method, text = nil, options = {}, &block)
  @template.label(@object_name, method, text, objectify_options(options), &block)
end

method = "_target_types_Test::Alpha"
ここでこっちが呼び出される

def label(object_name, method, content_or_options = nil, options = nil, &block)
  Tags::Label.new(object_name, method, self, content_or_options, options).render(&block)
end

その後なんやかんやあってmethod = "_target_types_Test::Alpha"がlabelのfor属性に設定される。(省略)

一方、inputのidについて追っていくと、inputタグの生成はcheck_box_without_bootstrapでRails側に投げており、ここで、valueの値("Test::Alpha")をsanitizedしたものがidとして設定される。

options["id"] += "_#{sanitized_value(tag_value)}"
def sanitized_value(value)
  value.to_s.gsub(/\s/, "_").gsub(/[^-\w]/, "").downcase
end

これで"Test::Alpha" -> "testalpha"に変換され、idが"_target_types_testalpha"になる。

Railsの仕様により、inputのidはsanitizedされたものが使われるが、rails-bootstrap-forms内でlabelのfor属性に当てる文字列をsanitizedしていなかったのが原因。

解決策

とりあえずの解決策としては、checkboxにidを指定する

<%= bootstrap_form_tag method: :get, url: search_path, layout: :horizontal do |f| %>
-  <%= f.check_box :target_types, { multiple: true, include_hidden: false, checked: params[:target_types].blank? || params[:target_types].include?("Test::Alpha") }, "Test::Alpha" %>
+  <%= f.check_box :target_types, { multiple: true, include_hidden: false, checked: params[:target_types].blank? || params[:target_types].include?("Test::Alpha"), id: "_target_types_Test::Alpha" }, "Test::Alpha" %>
-  <%= f.check_box :target_types, { multiple: true, include_hidden: false, checked: params[:target_types].blank? || params[:target_types].include?("Test::Beta") }, "Test::Beta" %>
+  <%= f.check_box :target_types, { multiple: true, include_hidden: false, checked: params[:target_types].blank? || params[:target_types].include?("Test::Beta"), id: "_target_types_Test::Beta" }, "Test::Beta" %>
  <%= f.submit "Search", class: 'btn btn-primary' %>
<% end %>

が、どう見てもrails-bootstrap-formsのバグなので、Pull Requestを送ろうかなあと思ったら既にあった
放置されてるっぽい…う〜ん。
いつのまにかMergeされてました。めでたしめでたし。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2