Railsでhas_manyの複数の画像をajaxを使って動的に一括登録する
概要
表題の通りのことをしたい。
ユーザーモデルと写真モデルは下記の様なhas_many
の関係とする。
ユーザー(user) ==has_many=⇛ 写真(photo)
簡単にやることをまとめると
field_for
を使用して、複数の子モデルを同時に保存するフォームを作成する。- 削除用のcheckboxを実装する。
- 追加ボタンを押したら動的にfile inputを増やす。
なお、画像登録はpaperclip
を使用する。
formの作成
photo_controller.rb
def new @user = User.find(params[:id]) if request.xhr? respond_to do |format| format.js end end end
new.html.slim
#選択したファイルのプレビューを表示 javascript: function readUploadImage(input) { if (input.files && input.files[0]) { var reader = new FileReader(); reader.onload = function (e) { $(input).next() .attr('src', e.target.result) .width(100); }; reader.readAsDataURL(input.files[0]); } } div = form_for(@user, url: form_path, html: {multipart: true}) do |f| .div =link_to 'ボタンを追加する', new_path(@user.id),remote: true .div data-role="photo_area" - @user.photos.each do |p| .div = image_tag p.image.url(:thumb) = check_box 'delete',"#{p.id}" = label_tag "delete[#{p.id}]", '削除' = render partial: 'photo_upload' .div = f.submit "登録する"
それぞれ説明をしていくと
=link_to 'ボタンを追加する', new_path(@user.id),remote: true
file inputをajaxで追加するリンクを実装する。
railsのlink_to
にremote: true
を設定するだけで、ajax通信が可能になる。
- @user.photos.each do |p| .div = image_tag p.image.url(:thumb) = check_box 'delete',"#{p.id}" = label_tag "delete[#{p.id}]", '削除'
更新時に既に写真が登録されていれば、その写真を表示し、同時に削除ボタンも実装する。
= render partial: 'add_photo_form'
_add_photo_form.html.slim
.div = fields_for "user[photos_attributes][#{Time.now.to_i}]" do |ff| = ff.file_field :image, onchange: "readUploadImage(this)" = image_tag ''
fields_for
を使用し、小モデルのinputを実装する。
なお、親モデルuser.rb
内でaccepts_nested_attributes_for
で子モデルを指定することを忘れずに。
accepts_nested_attributes_for :photos, allow_destroy: true
image_tag ''
はnew.html.slim
の一番上に記述されたjavascriptでプレビュー画像を表示するためのもの。
プレビュー画像はhtml5から実装されたFileRender
を使い、fileオブジェクトが保有するバッファの中身を読み込み、
それを上記のimage_tag
のsrc
に指定することで画像を表示させる。
ajaxでHTMLを追加する
=link_to 'ボタンを追加する', new_path(@car.id),remote: true
link_to
から、ajax送信でnew actionを実行する。
photo_controller.rb
if request.xhr? respond_to do |format| format.js end end
ajax送信があった場合は上記の通り、フォーマットはjsファイルでテンプレートを実行する。
new.js.slim
| $('[data-role="photo_area"]').append("#{escape_javascript(render partial: 'add_photo_form')}");
jsではappend()
でviewファイルadd_photo_form.html.slim
をセレクタで指定した箇所に追加する。
以上で下記のように動的にinputボタンを追加できるようになる。
保存する
photo_controller.rb
def update user = User.find(params[:id]) begin User.transaction do User.destroy(params[:delete] .select{ |k,v| v.to_i > 0}.keys) if params[:delete].present? user.update user_params if params[:user].present? end redirect_to redirect_path rescue => e p e.message end end
以上で、↓のように動的に写真を複数枚選択して、一括登録ができるようになる。