経緯
親モデルと同時に子モデルを保存するときに超便利なnested_form
というgemがあります。
https://github.com/ryanb/nested_form
たまたま、以下のような仕様を実装する機会がありました。
- 自由に子の数は変えられる
- 最大でも5個に抑える
- 何番目のフィールドかを表示する
その方法をば。
nested_formの使い方
こちらをご参照ください。
上限の決め方
実はやり方が公式に載っています。
How To: Limit max count of nested fields
ここにあるとおり、
$(function() { $(document).on('nested:fieldAdded', function(e) { var link = $(e.currentTarget.activeElement); if (!link.data('limit')) { return; } if (link.siblings('.fields:visible').length >= link.data('limit')) { link.hide(); } }); $(document).on('nested:fieldRemoved', function(e) { var link = $(e.target).siblings('a.add_nested_fields'); if (!link.data('limit')) { return; } if (link.siblings('.fields:visible').length < link.data('limit')) { link.show(); } }); })
をAssetとして読み込んでやるだけです。
個人的に、coffee
で全部書いてるのに一つだけjs
ってのも気持ちが悪いので、書き直したのがこちらです。
$ -> do window.nestedFormLimitter = -> $(document).on 'nested:fieldAdded', (e) -> $link = $(e.currentTarget.activeElement) if (!$link.data('limit')) return if ($link.siblings('div.fields:visible').length >= $link.data('limit')) $link.hide() $(document).on 'nested:fieldRemoved', (e) -> $link = $(e.target).siblings('a.add_nested_fields') if (!$link.data('limit')) return if ($link.siblings('div.fields:visible').length < $link.data('limit')) $link.show()
あとはViewファイルの追加ボタンに以下のようにdata-link
属性にリミットの個数を入れてやるだけです。
f.link_to_add '追加' :members, data: { limit: 5 }
フィールドの順番を表示する
フィールドの順番を表示するだけであれば、特に表示Ruby側で使えるようにする必要がないので、上記のCoffeeScript
に追記しました。
$ -> do window.nestedFormLimitter = -> # フィールドの番号を振って、表示する関数 do setFieldNum = -> $('div.fields:visible').each -> $('h3 span.order-num', $(@)).text($('div.fields:visible').index(this) + 1) $(document).on 'nested:fieldAdded', (e) -> setFieldNum() # 追記 $link = $(e.currentTarget.activeElement) if (!$link.data('limit')) return if ($link.siblings('div.fields:visible').length >= $link.data('limit')) $link.hide() $(document).on 'nested:fieldRemoved', (e) -> setFieldNum() # 追記 $link = $(e.target).siblings('a.add_nested_fields') if (!$link.data('limit')) return if ($link.siblings('div.fields:visible').length < $link.data('limit')) $link.show()
解説
nested_form
ではフォームが追加されたときはnested:fieldAdded
、削除されたときはnested:fieldRemoved
というイベントが発火します。
$linkはこのイベントを発火させたボタンのDOMが入っていて、一定数を超えているとき、非表示にして、同時に番号を表示させています。
ただ、ここで、一点注意なのは、クライアント側でDOMを操作してやるようなことをされてしまっては、必ずしも上限の個数のみのデータが飛んで来るわけではありません。
ちゃんと、モデル側でもバリデーションをかけてやりましょう。
以下は個数が大きすぎたときに弾くバリデーションの一例です。
class Group < ActiveRecord::Base validate :member_presence private def member_presence unless self.members.size <= 5 errors.add(:base, "メンバー多すぎ!エグザイルか!") end end end
仮にDOMを操作されても「メンバー多すぎ!エグザイルか!」と表示されます。1
まとめ
- 公式をよく見れば意外とちゃんと載ってる
- JSのバリデーションに頼りきらず、ちゃんとModelでもバリデーションを書く
以上で個数の上限を決めると同時にフィールドの番号を表示できるようになります。
公式のWikiはひとまず、チェックしてみる癖を付けてもいいかもしれませんね。
- 「Choo Choo TRAIN」を歌いながら女の子に「チューしてよ!」って叫んでた友達を思い出しました。 ↩
コメントを残す