はじめに
この記事ではRailsでアップロードした画像をJavascriptでプレビュー表示させる方法についてまとめています。すでに画像のアップロード自体はできている方を前提として書いておりますのでご了承ください。JSについては不慣れなところがあり恐縮です。
対象者
環境
- Ruby 3.2.3
- Rails 7.1.3.2
- esbuildで開発
- Tailwindcss使用
前提
- Active Storageの導入が完了している
- 画像のアップロードができている
- 1つのフォームでなく複数のフォームでプレビュー表示させたい方
↓参考記事
完成品
1つだけでなく複数のフォームに対応できます。
新規作成画面
プレビュー画像を1枚のみ表示します。
編集画面
すでに登録されている画像を表示させて、新しくアップロードされると変更されます。
プレビュー表示
ここではまずnew.html.erb
にプレビュー表示させる方法についてまとめます。
フォーム
フォームとプレビュー表示させる箇所にIDをつけます。
フォームにIDをつける
<%= form_with model: インスタンス変数, id: '任意_form' do |f| %>
プレビュー表示用
<div id="previews_任意" class="">
<% if @インスタンス変数.persisted? && @インスタンス変数.image.attached? %>
<div class="preview">
<%= image_tag @インスタンス変数.image, class: '' %>
</div>
<% end %>
</div>
「persisted?」と「attached?」は「もし保存されていて、画像がついていたら、」という意味で、条件に一致するならコードが実行されます。
このコードの記述箇所としては、アップロードフィールド(file_field)の近くで良いかと思います。
私の書いたコードはこちらです。
<!-- form_withにidをつけます -->
<%= form_with model: @tackle, id: 'tackle_form', class: 'md:mt-10 mt-5' do |f| %>
<!-- バリデーションエラーメッセージ -->
<%= render 'shared/error_messages', object: f.object %>
<div class='md:mt-10 mt-5'>
<%= f.label :name, class: 'text-black font-bold md:text-xl text-lg required' %>
<%= f.text_field :name, class: 'shadow appearance-none border-2 border-blue rounded w-full md:py-2 py-1 md:px-3 px-2 text-black leading-tight focus:outline-none focus:shadow-outline', placeholder: t('.placeholder.name') %>
</div>
<!-- ファイルのアップロードフィールド -->
<div class='md:mt-10 mt-5'>
<%= f.label :image, class: 'text-black font-bold md:text-xl text-lg' %>
<%= f.file_field :image, class: 'shadow appearance-none border-2 border-blue rounded w-full md:py-2 py-1 md:px-3 px-2 text-black leading-tight focus:outline-none focus:shadow-outline' %>
</div>
<!-- プレビュー表示用に記述 -->
<div class="md:mt-10 mt-5" id="previews_tackle">
<% if @tackle.persisted? && @tackle.image.attached? %>
<div class="preview">
<%= image_tag @tackle.image, class: 'preview-image' %>
</div>
<% end %>
</div>
<div class='md:mt-8 mt-4 text-center'>
<button type="submit" class="md:text-base text-sm btn font-bold bg-blue text-white hover:bg-blue-700 md:py-2 py-1 md:px-10 px-5 rounded focus:outline-none focus:shadow-outline"><%= t('.submit') %></button>
</div>
<% end %>
こちらを参考に、ご自身のインスタンス変数、クラスに合わせてください。
また、こちらのフォームはnew.html.erb
とedit.html.erb
で使うために「パーシャル/レンダリング」していますのでご注意ください。
JS
プレビューファイルを作成して読み込みます。
preview.js
app>javascript>preview.js
を作成します。
そして下記のように記述してプレビューを機能させます。
document.addEventListener('turbo:load', function() {
const forms = document.querySelectorAll('form[id$="_form"]');
forms.forEach(form => {
const fileField = form.querySelector('input[type="file"][name$="[image]"]');
if (!fileField) return;
const previewList = form.querySelector(`#previews_${form.id.split('_')[0]}`);
fileField.addEventListener('change', function(e) {
const alreadyPreview = previewList.querySelector('.preview');
if (alreadyPreview) {
alreadyPreview.remove();
}
const file = e.target.files[0];
const blob = window.URL.createObjectURL(file);
const previewWrapper = document.createElement('div');
previewWrapper.setAttribute('class', 'preview');
const previewImage = document.createElement('img');
previewImage.setAttribute('class', 'preview-image');
previewImage.setAttribute('src', blob);
previewWrapper.appendChild(previewImage);
previewList.appendChild(previewWrapper);
});
});
});
書いている内容はこちらの記事とほぼ同じですが、複数のフォームで使うために変えています。
一部のみ解説すると
- 2行目:idが
_form
で終わるフォームを全て取得
const forms = document.querySelectorAll('form[id$="_form"]');
- 4行目:
input[type="file"][name$="[image]"]
を持つファイル入力フィールドを探す
const fileField = form.querySelector('input[type="file"][name$="[image]"]');
具体的に<%= f.file_field :image, class: '' %>
を取得します。
- 6行目:プレビューを表示するためのエリアを特定し、idが
previews_
で始まる要素を取得
const previewList = form.querySelector(`#previews_${form.id.split('_')[0]}`);
同じWebページにおいてIDは一意(ユニーク) である必要があります。重複がないようにします。
preview.js読み込み
app>javascript>application.js
で読み込みます。
import "./preview"
プレビューサイズの調整
検証モードよりpreview-image
というクラスが当たっているので、直接CSSファイルに指定します。
.preview-image {
width: 100%;
height: auto;
}
ここは好きなようにCSSを当ててください。
おわりに
JSは難しいですが、なんとかできました。この記事がお役に立てば嬉しいです。
この記事を通じて気づいたことやご意見がありましたら教えてくださいますと幸いです。
参考