目標
以下のように、ネストした入力フォームをタブ表示したい。
環境
使用gem
-
rails-bootstrap-form
- railsで簡単にbootstrapのフォームデザインを使用するために使用
-
nested_form
- 動的にネストしたモデルのためのフォームを追加するために使用
今回の記事に関係のあるgemだけを列挙。
特に直接的に関係のあるものはnested_form。
その他注意事項
今回の記事のコード例はturbolinks
に対応していないのでご注意されたい。
フォームテンプレートファイルを用意する
以下のような形式でテンプレートファイルを作る。
- bootstrapのタブのパネル表示の文法に従い、
<ul>
と<div>
のclass
を設定する。 - CoffeeScriptでタブ追加ボタンをクリックした際の挙動を設定するため、それぞれに任意の
id
も設定しておく。 -
<ul>
の中身は後にCoffeeScriptで設定する。 -
f.link_to_add
の出力結果は使用しないが、追加する処理はnested_formを使用したいため、呼び出しだけはしておく。
_form.html.erb
<%= bootstrap_nested_form_for(@model_a, layout: :horizontal, label_col: "col-xs-2", control_col: "col-xs-10") do |f| %>
<%= f.text_field :column_a, label: "カラムA" %>
<%= f.text_field :column_b, label: "カラムB" %>
.
.
.
<ul class="nav nav-tabs" role="tablist" id="model-b-tab-list">
</ul>
<div class="tab-content" id="model-b-tab-contents">
<%= f.fields_for :nested_model_tables, wrapper: false do |nf| %>
<div class="tab-pane fields" role="tabpanel">
<%= nf.text_field :column_a, label: "カラムA" %>
<%= nf.text_field :column_b, label: "カラムB" %>
.
.
.
</div>
<!-- link_to_addを呼び出したいだけで、出力結果は使用しないので、<%= %>ではなくて<% %>で囲む。 -->
<% f.link_to_add "タブ追加", :part_number_sequences, data: { target: "#model-b-tab-contents" } %>
<% end %>
</div>
<% end %>
CoffeeScriptでnested_formに足りない機能を補完する
model_a.coffee
$ ->
$(document).on 'ready', ->
nested_form_tab('model-b-tab-list', 'model-b-tab-contents', 'タブ', '<i class="glyphicon glyphicon-plus"></i>', 'nested_model_tables')
$("#model-b-tab-contents").children('div').first().addClass('active')
$(document).on 'nested:fieldAdded:nested_model_tables', (event) ->
nested_form_tab('model-b-tab-list', 'model-b-tab-contents', 'タブ', '<i class="glyphicon glyphicon-plus"></i>', 'nested_model_tables')
$("#model-b-tab-contents").children('div').removeClass('active')
event.field.addClass('active')
nested_form_tab = (target_ul_id, target_div_id, tab_name_prefix, add_tab_name, assoc) ->
$("##{target_ul_id}").empty()
for value, index in $("##{target_div_id}").children('div')
$(value).attr('id', "#{target_div_id}-#{index + 1}")
$("##{target_ul_id}").append($("<li role='presentation'><a aria-controls='#{target_div_id}-#{index + 1}' href='##{target_div_id}-#{index + 1}' data-toggle='tab' role='tab'>#{tab_name_prefix}#{index + 1}</a></li>"))
$("##{target_ul_id}").append($("<li role='presentation'><a data-target='##{target_div_id}' class='add_nested_fields' data-association='#{assoc}' data-blueprint-id='#{assoc}_fields_blueprint' href='javascript:void(0)'>#{add_tab_name}</a></li>"))
ページが読み込まれた際とタブ追加ボタンが押された際に、
- 存在するフォームの数だけ、タブを追加する
- タブ追加ボタンを追加する
- ページ読み込み時には一個目のフォームに、フォーム追加時は追加したフォームに
addClass('active')
を実行する
まとめ
以上の作業を行うと、ネストしたモデルの入力フォームをタブ表示できるようになる。
ページ読み込み時にはタブ1がアクティブになり、タブ追加時には追加したタブがアクティブになる。