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 | イベント発火時の処理定義 |
data-controller
, data-target
, data-action
の使い方
Stimulusの基本:- 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を用いたローディングアニメーション)
筆者のローディングアニメーション
install
- Stimulusは、Rails7で
rails new
をする際に自動でインストールされている。- JSの管理にWebpackを使っている場合は、
package.json
に“@hotwired/stimulus”が記述されている。
- JSの管理にWebpackを使っている場合は、
- 【確認】
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
して下さい。
↓
この時点で以下の検証ができるはずです。
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
- 以降の記述で、
XXXTarget
を使用可能にする記述 - Stimulus公式 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