3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

RailsとActive Storageを使った登録画面と編集画面で即時プレビュー!

Last updated at Posted at 2023-06-16

ActiveStorageで登録画面や編集画面でプレビューする方法

自分の環境をご紹介

ActiveStorageのセットアップは行っている前提

  • rails7
  • ruby3.0.2
  • Bootstrap5とicon
  • JavaScriptはバニラJSです
  • AWS S3で画像を保存してます(ここはassets/imagesで保存した画像でもできます)
  • ActiveStorageでhas_one_attachedで1つのモデルに1つの画像です。

結論からどんなのができるのか?

新規登録側

新規登録ではS3に保存したデフォルトの画像が最初に表示されます
デフォルトで設定しないとArgumentErrorが出ますので必ずデフォは用意してください
デフォルトは、assets/imagesで保存した画像でもできます。
スクリーンショット 2023-06-16 23.39.27.png

画像を選択するとプレビューがパッと変わります。

スクリーンショット 2023-06-16 23.15.42.png

これの何がいいかと言いますと個人的にかっこいいからです。

ってのは冗談で、単純に選択した画像がその場ですぐわかるからですね!

編集画面

スクリーンショット 2023-06-16 23.42.38.png

もちろんこのように元々保存されている画像もプレビューできます!

画像選択後は新規登録と同じです!

やり方について1つずつ説明します

フォームから

一様報告しておきます、画像のinput部分のみです。

_form.html.erb
<div class='mb-3'>
  <i class="bi bi-image"></i>
  <%= f.label :image %>
  <%= f.file_field :image, accept: "image/*", id: 'image' %>
  <div class='mt-3'>
    <% if product.image.attached? %>
      <%= image_tag(product.image.variant(resize_to_limit: [200, 200]), id: 'image-preview', class: 'rounded image-resize') %>
    <% else %>
      <%= image_tag(Rails.application.credentials.dig(:aws, :s3, :default_image_url), id: 'image-preview', class: 'rounded image-resize') %>
    <% end %>
</div>

<%= f.file_field :image, accept: "image/*", id: 'image' %>これで画像を選択する
部分ができます、has_one_attached :imageで設定しているため:imageです
accept: "image/*"これは、クライアント側で全ての画像形式のみ受け付けるようにしています。
動画とかはダメですよ〜!と検証ができます。
id: 'image'はJavaScriptでDOM操作で使います。
<% if product.image.attached? %>は、attached?メソッドでActiveRecordオブジェクトが
データベースに保存されているかどうかを確認してくれます。
編集用と新規用で条件分岐しています。
新規は登録されていないのでfalseを返します、よって

<%= image_tag(Rails.application.credentials.dig(:aws, :s3, :default_image_url), id: 'image-preview', class: 'rounded image-resize') %>

ここのコードの処理が行われます。
Rails.application.credentials.dig(:aws, :s3, :default_image_url)
ここの部分はS3で保存した
画像をrails credentialscredentials.yml.encという暗号化された設定ファイルを使って
セキュアな情報を保存しています。(そのままURLを見せないようにする対策ですね!)
id: 'image-preview', class: 'rounded image-resize'
一気に説明します!
idは先ほどと同じJSのDOM操作で使います。
classはBootstrapを使用し、画像に少し丸みのある角にしています。
image-resizeは下で説明してます、カスタムCSSです!

お次はDBに保存されている方、persisted?メソッドでtrueを返している方ですね

編集画面の時のプレビューです。

<%= image_tag(product.image.variant(resize_to_limit: [200, 200]), id: 'image-preview', class: 'rounded image-resize') %>

product.image.variant(resize_to_limit: [200, 200])は、productモデルから
保存された画像を200x200にリサイズしてくれます。

後はほぼ先ほどと同じです!

<%= image_tag(product.image.variant(resize_to_limit: [200, 200]), id: 'image-preview', class: 'rounded image-resize') %>
なんでこっちもリサイズをカスタムで設定しているの??
variant(resize_to_limit: [200, 200]でリサイズしてますよね?
説明しよう!
variantはActiveStorageが画像を処理する際に使用するメソッドで、
保存される画像の大きさをリサイズしてくれます
着目して欲しいところは保存です
画面を選択しているときは、保存されておりません。
よって選択によってプレビューしている画像も一緒の大きさにしたいのでカスタムCSS使ってます。

保存されていない画像サイズ変更用のCSS

application.css
.image-resize {
  max-width: 200px;
  max-height: 200px;
  object-fit: cover;
}

最大横縦幅を200pxにしています。
object-fit: coverで画像のアスペクト比が保持しつつ指定した
幅に気持ちよくフィットするようにしています

JavaScriptです

preview.js
document.addEventListener('turbo:load', () => {
  const imageInput = document.querySelector('#image');
  const imagePreview = document.querySelector('#image-preview');

  imageInput.addEventListener('change', (e) => {
    const file = e.target.files[0];
    const reader = new FileReader();

    reader.onloadend = () => {
      imagePreview.src = reader.result;
    };

    if (file) {
      reader.readAsDataURL(file);
    } else {
      imagePreview.src = "";
    }
  });
});

turbo:loadにすることで,Hotwireが新しいページを読み込み終えた時に発生しその後に実行する関数を定義してます。

これは非常に大事でturbo:loadを設定しないと
いちいち画面をリロードしないとJavaScriptが反映されません。

const imageInput = document.querySelector('#image');
const imagePreview = document.querySelector('#image-preview');

この部分は先ほどのHTMLで設定した、idを指定してそれぞれの定数に保存しています

imageInput.addEventListener('change', (e) => {
    const file = e.target.files[0];
    const reader = new FileReader();

    reader.onloadend = () => {
      imagePreview.src = reader.result;
    };

imageInput要素(画像を選択する方)でchangeイベントを発生したときに実行する関数を定義しています
ここでのchangeイベントは、userが新しいファイルを選択したときに発生するイベントです。

const file = e.target.files[0];インデックス番号0番、よって最初に選択された
ファイルを取得しfileという定数に保存してます

const reader = new FileReader();
FileReaderオブジェクトは、ユーザーが選択したファイルを読み込むために使用されます。
それをrender定数に保存

reader.onloadend = () => {
  imagePreview.src = reader.result;
};

ファイルを読み込んだ後に実行する関数を書いています
FileReaderオブジェクトが読み込んだ結果(画像URL)をimagePreview要素(img要素)の
src属性に設定しています。これにより、選択された画像のプレビューが表示されます。
まとめると、読み込んだファイルの結果をsrc属性に設定しプレビューしてます。

<img src=''>srcの中身を読み込んだ画像のURLを動的に変えてるイメージです!

次はuserがファイルを選択している時としていない時の条件分岐です。

if (file) {
  reader.readAsDataURL(file);
} else {
  imagePreview.src = "<%= Rails.application.credentials.dig(:aws, :s3, :default_image_url) %>";
}

簡単にいうと、選択しているとファイルのデータをURLとして読み込むように

選択していないときはデフォルト画像を表示するようにしています。

今回の場合選択していない時というのは新規登録の時です!

最後に

前から実装してみたい機能だったので無事に実装できてよかったです!!

お疲れ様でした!

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?