LoginSignup
3
3

More than 5 years have passed since last update.

nested_form + bootstrapでネストしたモデルの入力フォームをタブ表示する

Last updated at Posted at 2017-10-11

目標

以下のように、ネストした入力フォームをタブ表示したい。

1.png

環境

使用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がアクティブになり、タブ追加時には追加したタブがアクティブになる。

3
3
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
3
3