🤢

Stimulusで簡単!daisyUIとRailsを使ったローディングアニメーション演出

に公開

📘 この記事について

本記事は、StimulusやHotwireを学び始めたばかりの筆者(初学者)が、自分の学習内容を整理しながらまとめた初心者向けの解説記事です。
特にRails 7と相性のよいStimulusの導入方法や、daisyUIと組み合わせたローディングアニメーションの実装例について、できるだけ丁寧にステップごとに紹介しています。

同じようにこれから学び始める方の参考になれば幸いです。不明点や改善点などがあれば、ぜひご指摘いただけると嬉しいです!

Stimulus 解説(Rails + Hotwire向け)

Stimulusとは?

Stimulus公式:https://stimulus.hotwired.dev/

Stimulusは、「HTMLに少しのJavaScriptの力を加えて、Webページに動きを持たせることができる軽量なJavaScriptフレームワーク」です。

「少ないコードで、動きのあるUIを作りたい」

そんなときに活躍します。

Hotwireとの関係性

Hotwire公式:https://hotwired.dev/

Stimulusは、Hotwireというフロントエンド技術群の中の1つです。

Hotwireの基本構成

名前 構成
Turbo HTMLを使ってページ遷移や一部更新を迅速に行う
Stimulus UIに動きをつけるための軽量JavaScriptフレームワーク
Strada モバイルアプリでWebコンポーネントを活かすための技術(開発中)

HotwireはJavaScriptを書く量を最小限にしつつ、SPAのような体験を可能にする。

Stimulusの特徴

  • JavaScriptの知識が少なくても使いやすい
  • コントローラーごとにロジックを整理でき、保守性が高い
  • HTML(data-xxx属性)中心で開発できる
  • Turboとの相性が抜群(Rails 7で標準搭載)

基本構成と用語

Stimulusは、以下の3つを中心に構成されている。

Stimulusの基本構成

要素 役割
Controller 各コンポーネントのJSの定義(クラス)。特定のDOM要素に対するJavaScriptの機能を担当する。
Target 操作対象のDOM操作
Action イベント発火時の処理定義

Stimulusの基本:data-controller, data-target, data-action の使い方

  • Stimulusは、HTMLのカスタムデータ属性data-xxx)を使って、JavaScriptのコントローラーとDOMを直感的に結びつけられる軽量フレームワークです。
  • つまり、これらの属性を使用することで、特定のDOM要素に対してStimulusJSのコントローラーを関連付けたり、イベントリスナーやアクションを定義したりすることができます。

本記事では、以下の3つの属性について、実例を交えて分かりやすく解説します。

  • data-controller
  • data-target
  • data-action

data-controller とは?

  • data-controller 属性を使うことで、Stimulusのコントローラーを特定のHTML要素に紐づけます。
<div data-controller="hello">
  ...
</div>

→ 上記では hello_controller.js にある Stimulus コントローラーがこの要素に適用されます。

  • この場合、hello_controller.js というファイルが読み込まれ、その中のクラスがこの要素と紐づきます。

data-target とは?

  • data-<controller名>-target 属性は、コントローラーから参照・操作したいDOM要素を指定します。
<input data-hello-target="name" type="text">
<span data-hello-target="output"></span>

hello_controller.jsに以下のように書くことでアクセスできる。

static targets = [ "name", "output" ]

greet() {
  this.outputTarget.textContent =
    `Hello, ${this.nameTarget.value}!`
}
  • this.nameTarget → <input> の内容を取得
  • this.outputTarget → <span> に出力

data-action とは?

  • data-action 属性を使うことで、特定のイベント発火時にコントローラー内のメソッドを実行できます。

【構文】

イベント名 -> コントローラー名.メソッド名
<button data-action="click->hello#greet">
  Greet
</button>
  • この例では、ボタンがクリックされると hello_controller.js の greet() メソッドが呼び出されます。

サンプル:挨拶を表示する簡単なアプリ:https://stimulus.hotwired.dev/

HTML側

<div data-controller="hello">
  <input data-hello-target="name" type="text">

  <button data-action="click->hello#greet">
    Greet
  </button>

  <span data-hello-target="output">
  </span>
</div>

コントローラー側

  • app/javascript/controllers/hello_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "name", "output" ]

  greet() {
    this.outputTarget.textContent =
      `Hello, ${this.nameTarget.value}!`
  }
}

Stimulusのよくあるユースケース

  • フォーム入力時の文字数カウント
  • モーダルウィンドウの開閉
  • タブ切り替え
  • Ajax送信後のUI更新
  • スクロール・ホバーイベントの制御
  • Turboとの併用でリアルタイムUI制御(例:フォームのバリデーション)
  • ローディングアニメーション

まとめ

Stimulusは、「ちょっとしたUIの動き」をHTMLベースで簡単に追加できる、Railsに最適なJavaScriptフレームワークです。

複雑なSPAを避けたいけど、UIに動きが欲しいときにぴったり!

実装(Stimulusを用いたローディングアニメーション)

筆者のローディングアニメーション

Image from Gyazo

install

  • Stimulusは、Rails7でrails newをする際に自動でインストールされている。
    • JSの管理にWebpackを使っている場合は、package.jsonに“@hotwired/stimulus”が記述されている。
  • 【確認】Gemfileに以下のような記述があるはず。
    gem "stimulus-rails"
    

stimulus-rails公式:https://github.com/hotwired/stimulus-rails/

Terminal

  • 以下のコマンドを実行
bin/rails g stimulus loading

実行により以下の内容が行われます。

  • app/javascript/controllers/index.jsへの自動追記
  • app/javascript/controllers/loading_controller.jsの作成

【検証】Stimulusが動いているか?

まずStimulusが動くか確認しましょう!

初期の段階に生成されるapp/javascript/controllers/hello_controller.js

上記リンクにて記載されている記述を行って動作確認します。

次に、確認の段階でStimulusが動いてない場合(Stimulusの例のようにボタンを押しても”Hello, XXX!”と表示されないなど)は、各々の状況に応じて以下の公式の作業手順を試してみてください。

ここでは、「RailsでStimulusを使用する(stimulus-rails gemの使用)」場合の前提で進めていきます。

まず、Gemfileにgem "stimulus-rails”が記載されてあるか確認します。
基本的には、デフォルトで入っていると思います。
記述が見当たらない場合は、gemを記述して、bundle installして下さい。

この時点で以下の検証ができるはずです。

https://stimulus.hotwired.dev/

inputタグに入力した値とHelloが、ボタンを押して表示されれば成功!!

もしStimulusが動作していない様子(Stimulusの例のようにボタンを押しても”Hello, XXX!”と表示されない場合)であれば、以下のコマンドを実行します。

bin/rails stimulus:install
  • Stimulusのセットアップコマンドです。
  • 実行中にコンフリクトした場合、内容を復元できる方は全て「Y」にして、上書きしてしまうのも良いかもしれません。

View

  • app/views/layouts/application.html.erb を以下のように編集
<body>
  <div data-controller="loading">
    <div data-loading-target="spinner" class="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-40 hidden">
      <div class="h-[150px] w-[150px] flex items-center justify-center">
        <span class="loading loading-spinner text-default w-full h-full"></span>
      </div>
    </div>

    <%= render 'shared/header' %>
    
    <%= yield %>
    
    <%= render 'shared/footer' %>
  </div>
</body>
  • ローディングアニメーションが全てのページでレンダリングされるように以下のように、data-controller="loading"の内側の要素に全ての要素が入るようにします。
  • bodyタグ直下にdata-controller="loading"を配置することで、ページ全体でStimulusのローディング制御が有効になります。
  • この中にあるボタンやリンクなどに data-action="click->loading#show"を指定すれば、スピナーなどのアニメーションが正しく動作します。

JavaScript

  • app/javascript/controllers/loading_controller.jsを以下のように編集
import { Controller } from "@hotwired/stimulus"

// Controller for showing loading spinner during form submission
export default class extends Controller {
  static targets = ["spinner"]

  connect() {
    this.hide()
  }

  show() {
    this.spinnerTarget.classList.remove("hidden")
  }

  hide() {
    this.spinnerTarget.classList.add("hidden")
  }
}
  • static targets
  • connect
    • ページ読み込み(command + ←で戻る時など)の際に、アニメーションが無限再生されない設定
  • show
    • ローディングアニメーションのスピナーが表示される設定
    • 対象の要素のクラスからhiddenを取り除く
  • hide
    • ローディングアニメーションのスピナーが非表示にされる設定
    • 対象の要素のクラスからhiddenを追加する

ローディングアニメーションの変更について

ローディングアニメーションだけでなく他の種類に変更したい場合は、上記のコードの「spinner」の部分(HTMLで2箇所、JSで3箇所の計5箇所)を全て別のクラス名のものに変更します。

daisyUI ローディングアニメーション:https://daisyui.com/components/loading/

View

各Viewの要素への属性付与

  • 各ビューファイルのイベントを発火させたいHTMLの要素(ボタンなど)の属性に(data: { action: "click->loading#show”など)を付与していきます。
  • 特定の要素には、デフォルトで対応するイベントが設定されている。例えば、aタグやbuttonなど。Stimulus公式 イベント対応表に詳細説明と対応表が載っています。

具体的に、今回は以下のような属性を付与していく。

data: { action: "click->loading#show" }

Viewへの付与の例示

  • form.submitの場合
<%= form.submit class: "btn btn-primary", data: { action: "click->loading#show" } %>

📌 ボタンではなくフォーム全体にアクションを指定する場合は、submit->loading#show のようにすると、ボタンクリック以外の送信方法(Enterキーなど)にも対応できます。

  • link_toの場合
<%= link_to "投稿一覧", root_path, class: 'btn btn-primary', data: { action: "click->loading#show" } %>

これで重い処理や開発者ツールのスロットリング設定などから、ローディングアニメーションの表示が確認できると思います!!!

備考

Discussion