0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Stimulusによる画像アップロード時のプレビュー表示

0
Posted at

はじめに

Webアプリケーションを作る際に、投稿機能に画像アップロード機能をつけました。その際、画像をアップロードしても投稿ボタンを押すまで画像がちゃんと貼られているのかわからない事態が起こりました。なので、画像をアップロードした際に即時的に画像のプレビューを表示することでその問題を解決することにしました。

完成のコード

まずは完成したコードの全体感を載せます。後にコードの解説をいたします。

<%= form_with model: @post do |form| %>

  <div data-controller="image-preview">

    <!-- 画像選択 -->
    <%= form.file_field :image,
      data: {
        image_preview_target: "input",
        action: "change->image-preview#preview"
      } %>

    <!-- プレビュー -->
    <img
      data-image-preview-target="preview"
      hidden
      width="200">

    <!-- 削除ボタン -->
    <button
      type="button"
      data-image-preview-target="removeButton"
      data-action="click->image-preview#remove"
      hidden>
      画像削除
    </button>

  </div>

  <%= form.submit "投稿" %>

<% end %>

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["input", "preview", "removeButton"]

  preview() {
    const file = this.inputTarget.files[0]

    if (!file) return

    const reader = new FileReader()

    reader.onload = (e) => {
      this.previewTarget.src = e.target.result
      this.previewTarget.hidden = false
      this.removeButtonTarget.hidden = false
    }

    reader.readAsDataURL(file)
  }

  remove() {
    this.inputTarget.value = ""
    this.previewTarget.src = ""
    this.previewTarget.hidden = true
    this.removeButtonTarget.hidden = true
  }
}

解説

まずは、ビューの要素とStimulusコントローラーを繋げる必要があります。

<div data-controller="image-preview">

この記述で今回使用する画像投稿時のプレビューを表示するコントローラー(image-preview_controller.js)と繋げることができます。

Targetの指定

JSの方で

static targets = ["input", "preview", "removeButton"]

と記述がありますが、こちらは実際の画像投稿フォームのHTML要素を指定することになります。
紛らわしいかもしれませんが、StimulusではHTML側からJSのどの要素に対応するか記述することになります。
実際に、

<img
  data-image-preview-target="preview"
  hidden
  width="200">

画像を表示させる要素からdata-image-preview-target="preview"といったようにJSのターゲットと紐づけています。

画像のプレビュー表示

const file = this.inputTarget.files[0]

ここで画像アップロードフォームから画像をfileという定数に格納します。[0]としているので画像ファイルは一枚を想定しております。

画像が存在する場合、

reader.onload = (e) => {
  this.previewTarget.src = e.target.result
  this.previewTarget.hidden = false
  this.removeButtonTarget.hidden = false
}

といった処理に移行します。
reader.onloadによって画像の読み込みが正常に終わった後に処理が行われます。
今イベントオブジェクトには画像の情報があります。
例えば、

this.previewTarget.src = e.target.result

この記述で、HTMLのpreview要素にsrc = 画像のURLが追加されるということになります。
同様に、preview要素とremoveButtonのhidden要素をfalseとするため、画像がアップロードされた場合、画像のプレビューと削除ボタンが表示されるということになります。

reader.readAsDataURL(file)

こちらで添付画像のurlが作られ、render.onloadに結果が返されます。

プレビュー画像削除

プレビュー画像が表示されている間、削除ボタンのhiddenがfalseになっているため、表示されクリックすることができるようになっています。

data-action="click->image-preview#remove"

これによってプレビュー画像を削除できるようになっております。
data-action属性でイベント→コントローラ名#メソッドを指定するとStimulusコントローラのメソッドを実行することができます。
では実際にStimulusコントローラのremoveメソッドを見てみると

remove() {
  this.inputTarget.value = ""
  this.previewTarget.src = ""
  this.previewTarget.hidden = true
  this.removeButtonTarget.hidden = true
}

""を代入することで、すでに添付されていた画像の情報を消し、hiddenをtrueにすることでプレビュー部分とボタンの部分を非表示にしています。

まとめ

  • data-controllerでhtmlの要素とコントローラを接続する
  • イベント→コントローラ名#メソッドでコントローラのメソッドを動かす
  • FileReader を使うことでクライアント側で画像プレビューが可能

画像がアップロードされているか確認できるシンプルなフロント側の制御ができます。
プレビュー機能以外でもStimulusはシンプルなフロント制御ができるので、様々なことに応用していこうと思います。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?