はじめに
以前に最大1枚の画像プレビュー機能について書きましたが、
この記事では、その応用として画像の複数投稿を実装するまでを書いていきます。
[Rails]画像選択時にプレビュー表示
albumモデル(親)
photoモデル(子)
今回はアルバム名をalbumsテーブルのnameカラムに、
写真をphotosテーブルのimageカラムに保存していきます。
- Ruby 2.5.1
- Ruby on Rails 5.0.1.7
- Carrierwave Gem
- 画像投稿は最大5枚
- 今回はalbumモデルを渡した1つのフォームの中でfields_forを用いてphtosテーブルへの保存も行う
なお、Carrierwaveの設定は今回の記事では割愛させていただきます。
アソシエーションを組む
今回はfields_forを使用するため以下の記述となります。
fields_forについては、また別の記事に書こうと思います。
has_many :photos
accepts_nested_attributes_for :photos, allow_destroy: true
belongs_to :album
allow_destroy: true
accepts_nested_attributes_forメソッドのオプションとして、引数に書くことができる記述です。このオプションをつけることで、親のレコードが削除された場合に、関連付いている子のレコードも一緒に削除してくれます。
コントローラーの編集
new, createアクション
フォームで使用するインスタンスを作成します。
今回は保存の結果次第でフラッシュメッセージを出してみます。
def new
@album = Album.new
@album.photos.new # Albumクラスのインスタンスに関連づけられたPhotoクラスのインスタンスが作成されます。
end
def create
@album = Album.new(album_params)
unless @album.valid?
flash[:alert] = "入力情報を確認してください"
redirect_to new_album_path
end
@album.save
flash[:notice] = "保存が完了しました"
redirect_to root_path
end
ストロングパラメータ
def album_params
params.require(:album).permit(:name, photos_attributes: [:image])
end
photos_attributes: [:image]
fields_forを利用して作成されたフォームから来る値は、○○s_attributes: [:××]という形でparamsに入ります。○○は関連付く側のモデルの名前、××にはフォームに対応するカラムの名前が入ります。
フォームの編集
次に、hamlを用いてフォームを作成していきます。
なお、フォーム本体は隠してます。
.label-boxをクリックする度に随時、.label-boxが対応するファイルを切り替えていきます。
そのため投稿枚数に関わらず、ユーザーは同じボタンのみを押せばいいことになります。
= form_for @album do |f|
.names
.names__inner
.teams
%h3.team
アルバム名
= f.text_field :name, placeholder: "アルバム名"
.image
.image__inner
.teams
%h3.team
写真
.image__inner__container
.prev-content
/ ここにプレビュー画像を表示していきます
.label-content
/ ユーザーはここのみをクリックします
%label{for: "album_photos_attributes_0_image", class: "label-box", id: "label-box--0"}
%pre.label-box__text-visible クリックしてファイルをアップロード
%p 最大5枚までアップロードできます
.hidden-content
= f.fields_for :photos do |i|
= i.file_field :image, class:"hidden-field"
%input{class:"hidden-field", type: "file", name: "album[photos_attributes][1][image]", id: "album_photos_attributes_1_image" }
%input{class:"hidden-field", type: "file", name: "album[photos_attributes][2][image]", id: "album_photos_attributes_2_image" }
%input{class:"hidden-field", type: "file", name: "album[photos_attributes][3][image]", id: "album_photos_attributes_3_image" }
%input{class:"hidden-field", type: "file", name: "album[photos_attributes][4][image]", id: "album_photos_attributes_4_image" }
最大5枚までの投稿ができるため、最初から5つのフォームを用意して隠しておきます。
%input{class:"hidden-field", type: "file", name: "album[photos_attributes][0][image]", id: "album_photos_attributes_0_image" }
fields_forで生成されるフォームの1つ目は、このようにidやclassに0という添え字が当てられます。2つ目のフォーム以降は、この添え字の部分を1,2...といった具合に増やしていけば大丈夫です。
idやclassの数字に応じて.label-boxを対応させるようにJSファイルを編集していきます。
jsファイルの編集
$(document).on('turbolinks:load', function () {
$(function () {
function buildHTML(count) {
var html = `<div class="preview-box" id="preview-box__${count}">
<div class="upper-box">
<img src="" alt="preview">
</div>
<div class="lower-box">
<div class="delete-box" id="delete_btn_${count}">
<span>削除</span>
</div>
</div>
</div>`
return html;
}
// ファイルに画像が選択された瞬間に発火します
$(document).on('change', '.hidden-field', function () {
var id = $(this).attr('id').replace(/[^0-9]/g, '');
$('.label-box').attr({ id: `label-box--${id}`, for: `album_photos_attributes_${id}_image` });
var file = this.files[0];
var reader = new FileReader();
$('.hidden-btn').show();
reader.readAsDataURL(file);
reader.onload = function () {
var image = this.result;
if ($(`#preview-box__${id}`).length == 0) {
var count = $('.preview-box').length;
var html = buildHTML(id);
var prevContent = $('.label-content').prev();
$(prevContent).append(html);
}
$(`#preview-box__${id} img`).attr('src', `${image}`);
var count = $('.preview-box').length;
// 画像が5枚選択された場合、投稿ボタン(.label-content)を隠します
if (count == 5) {
$('.label-content').hide();
}
if (count < 5) {
$('.label-box').attr({ id: `label-box--${count}`, for: `album_photos_attributes_${count}_image` });
}
}
});
$(document).on('click', '.delete-box', function () {
var count = $('.preview-box').length;
var id = $(this).attr('id').replace(/[^0-9]/g, '');
$(`#preview-box__${id}`).remove();
});
});
});
おわり
今回は新規投稿でしたが、次回はそのデータの編集パターンも書ければと思います。
最後まで見ていただき、ありがとうございました。