※2022年から技術系の記事は個人ブログに投稿しております。ぜひこちらもご覧ください→yamaday0u Blog
Ruby on RailsのアプリでJavaScriptを使ってプレビュー機能を実装するコードを解説します。
こんな感じで画像ファイルを選択すると、プレビューが表示される機能を目指します。
実行環境
Rails 6.0.3.1
macOS Catalina バージョン10.15.7
ビューのコード
ビューはきわめてシンプルです。
app/views/messages/_form.html.erb
<%= form_with model: @message, id: 'new_message', local: true do |f| %>
<%= f.text_field :content, placeholder: 'type a message' %>
<%= f.file_field :image %>
<%= f.submit '送信' %>
<div id="image-list"></div><%# プレビューを表示する部分 %>
<% end %>
<%# ↓投稿編集ページ用の画像表示箇所↓%>
<%= image_tag @message.image, id: 'image' if @message.image.present? %>
JavaScriptのコード
コードは以下の通りです。流れをシンプルにして説明すると、
- 画像ファイルが選択されるとその画像ファイルに対してURLが生成
- 生成したimg要素のsrc属性にそのURLをセット
- プレビュー表示用のdiv要素の中に子要素のdiv要素とimg要素を追加する
この流れでプレビュー画像を表示します。
app/javascript/packs/preview.js
// プレビュー表示機能は新規投稿("/new/")か投稿編集("/edit/")ページでのみ有効にする
if (document.URL.match( /new/ ) || document.URL.match( /edit/ )) {
document.addEventListener('DOMContentLoaded', function(){
// プレビューを表示するための要素を取得
const ImageList = document.getElementById('image-list');
const createImageHTML = (blob) => {
// 画像を表示するためのdiv要素を生成
const imageElement = document.createElement('div');
// 表示する画像を生成
const blobImage = document.createElement('img');
// img要素のsrc属性の値をセット
blobImage.setAttribute('src', blob);
// 生成したHTMLの要素をブラウザに表示させる
imageElement.appendChild(blobImage);
ImageList.appendChild(imageElement);
};
document.getElementById('message_image').addEventListener('change', function(e){
// 画像が表示されている場合のみ、すでに存在している画像を削除する(編集ページ用)
const imageContent = document.querySelector('img');
if (imageContent){
imageContent.remove();
}
// 発火したイベントeの中の、targetの中の、filesという配列に格納された画像を変数に代入
const file = e.target.files[0];
// 画像のURLを生成
const blob = window.URL.createObjectURL(file);
createImageHTML(blob);
});
});
}
ビューファイルとJavaScriptのファイルを見比べやすいように横並びにした画像を用意しました。
最後に、application.jsに以下の記述を追記して、turbolinksはコメントアウト(または削除)し、preview.jsを読み込むことを忘れないようにしましょう。
app/javascript/packs/application.js
require("@rails/ujs").start()
// require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require("./preview") //このコードを追記