[Rails]stimulus-componentsを使ってみる
はじめに
stimulus-components
を使って開発中のRailsアプリのフロント部分を作っていきます。
stimulus-components
は、Stimulus
と組み合わせたフロントエンドコンポーネントのライブラリです。
Stimulus
は軽量なJavaScriptフレームワークであり、HTMLに直接カスタムデータ属性を使ってコントローラーやアクションを指定することで、特定のDOM要素に対してJavaScriptの機能を追加することができます。
環境
Rails 7.0.4.3
ruby 3.2.1
stimulus
をインストール
stimulus
が必要です。ない場合インストールしましょう。
yarn add @hotwired/stimulus
<!-- HTMLでCDNを使用する場合 -->
<script src="https://unpkg.com/stimulus@2.0.0/dist/stimulus.umd.js"></script>
Stimulusの使い方を簡単に説明します。
Stimulusのコントローラーを作成する
rails 7でrails new
したのでデフォルトで入ってるapp/javascript/controllers/hello_controller.js
を使って説明します。
Stimulusのコントローラーを定義する
コントローラーは特定のDOM要素に対するJavaScriptの機能を担当します。
data-xxx
は、StimulusJSを使用する際にHTMLに追加するカスタムデータ属性(Custom Data Attributes)です。
これらの属性を使用することで、特定のDOM要素に対してStimulusJSのコントローラーを関連付けたり、イベントリスナーやアクションを定義したりすることができます。
data-controller
属性には、コントローラーのクラス名を指定します。
data-target
:
- コントローラーのクラス定義で
static targets
プロパティを使用して指定された要素の名前を記述します。 - コントローラーのアクション内で、
this
キーワードを使用してdata-target
で指定された要素にアクセスすることができます。 -
data-target
にはCSSセレクターを指定します。
data-action
:
-
data-action
は、イベントリスナーやアクションを定義するための属性です。 - この属性には、イベントとアクションを関連付けるための構文を記述します。
- イベントリスナーやアクションの構文は、
イベント名->コントローラー名.アクション名
という形式です。 - コントローラーのクラス定義で対応するアクションを定義しておく必要があります。
StimulusJSのコントローラーであるhello
を<div>
要素に適用し、data-action
属性を使って<button>
要素にclick
イベントを追加し、hello
コントローラーのgreet
アクションに関連付けています(click->hello.greet
)。
<!-- HTML -->
<div data-controller="hello">
<input type="text" data-target="hello.name" placeholder="名前を入力してください">
<button data-action="click->hello.greet">挨拶</button>
</div>
hello
コントローラーのクラス定義でstatic targets
プロパティを使用して、<input>
要素とname
という名前のターゲットを指定します:
import { Controller } from 'stimulus';
export default class extends Controller {
static targets = ['name'];
greet() {
const name = this.nameTarget.value;
alert(`こんにちは, ${name}さん!`);
}
}
このように、StimulusJSではHTMLの属性を活用してJavaScriptの機能を管理するため、直感的で分かりやすいコードを記述できます。Railsと組み合わせることで、効率的かつメンテナンス性の高いJavascriptを書くことができます。
今回ではドロップダウンメニューのコンポーネントをインストールしアプリに入れたいと思います。
stimulus-components
をインストールする
アプリに必要なコンポーネントだけをインストールすることができます。
importmap
でインストールします。
bin/importmap pin stimulus-dropdown
Pinning "stimulus-dropdown" to https://ga.jspm.io/npm:stimulus-dropdown@2.1.0/dist/stimulus-dropdown.mjs
Pinning "@hotwired/stimulus" to https://ga.jspm.io/npm:@hotwired/stimulus@3.2.1/dist/stimulus.js
Pinning "hotkeys-js" to https://ga.jspm.io/npm:hotkeys-js@3.11.2/dist/hotkeys.esm.js
Pinning "stimulus-use" to https://ga.jspm.io/npm:stimulus-use@0.51.3/dist/index.js
dropdown用JSコントローラーを作成する
bin/rails g stimulus dropdown
create app/javascript/controllers/dropdown_controller.js
import Dropdown from 'stimulus-dropdown'
export default class extends Dropdown {
connect() {
super.connect()
}
toggle(event) {
super.toggle()
}
hide(event) {
super.hide(event)
}
}
-
connect()
: コントローラーがDOMに接続されたときに実行されるメソッドです。親クラスであるDropdown
コントローラーのconnect()
メソッドを呼び出して、デフォルトの接続処理を保持します。 -
toggle(event)
: ドロップダウンメニューを開閉するためのメソッドです。親クラスであるDropdown
コントローラーのtoggle()
メソッドを呼び出して、デフォルトの開閉処理を実行します。toggle(event)
メソッドは、ドロップダウンメニューの開閉がトリガーされるイベント(例: ボタンのクリック)を受け取ることができるように、event
引数を受け取っています。 -
hide(event)
: ドロップダウンメニューを非表示にするためのメソッドです。親クラスであるDropdown
コントローラーのhide(event)
メソッドを呼び出して、デフォルトの非表示処理を実行します。hide(event)
メソッドは、非表示がトリガーされるイベント(例: ウィンドウのクリック)を受け取ることができるように、event
引数を受け取っています。
データ属性を設定する
TailwindCSS
のドロップダウンコンポーネントを使ってます。
<div class="relative inline-block text-left" data-controller="dropdown">
<div>
<button type="button" data-action="dropdown#toggle click@windown->dropdown#hide" class="inline-flex w-full justify-center gap-x-1.5 rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50" id="menu-button" aria-expanded="true" aria-haspopup="true">
メニュー
<svg class="-mr-1 h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd" />
</svg>
</button>
</div>
<div class="absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="menu-button" tabindex="-1"
data-dropdown-target="menu"
class="hidden transition transform origin-top-right absolute right-0"
data-transition-enter-from="opacity-0 scale-95"
data-transition-enter-to="opacity-100 scale-100"
data-transition-leave-from="opacity-100 scale-100"
data-transition-leave-to="opacity-0 scale-95"
>
<div class="py-1" role="none">
<% if user_signed_in? %>
<%= link_to 'プロフィール', edit_user_registration_path,
class: "text-grey-700 block px-4 py-2 text-sm",
role: "menuitem", tabindex: "-1", id: "menu-item-0",
data:{ action: "click->dropdown#toggle", target: "dropdown" } %>
<%= button_to 'ログアウト', destroy_user_session_path, method: :delete,
class: "text-grey-700 block px-4 py-2 text-sm",
role: "menuitem", tabindex: "-1", id: "menu-item-3",
data:{ action: "click->dropdown#toggle", target: "dropdown" } %>
<% else %>
<%= link_to 'ログイン', new_user_session_path,
class: "text-grey-700 block px-4 py-2 text-sm",
role: "menuitem", tabindex: "-1", id: "menu-item-2",
data:{ action: "click->dropdown#toggle", target: "dropdown" }%>
<%= link_to '新規登録', new_user_registration_path,
class: "text-grey-700 block px-4 py-2 text-sm",
role: "menuitem", tabindex: "-1", id: "menu-item-2",
data:{ action: "click->dropdown#toggle", target: "dropdown" } %>
<% end %>
</div>
</div>
</div>
data-action="dropdown#toggle click@windown->dropdown#hide"
が2つのアクションを指定していますね。
dropdown#toggle
: ドロップダウンメニューを開閉するアクションを示しています。
dropdown
はコントローラー名を表し、toggle
はコントローラー内で定義されたtoggle
アクションを指します。
click@window->dropdown#hide
: ウィンドウのクリックイベントに対してドロップダウンメニューを非表示にするアクションを示しています。
これは特定のDOM要素ではなく、ウィンドウ全体に対して定義されているアクションです。click@window
は、ウィンドウのクリックイベントに対応しています。
dropdown#hide
は、コントローラー名dropdown
によって呼び出されるhide
アクションを指します。
トースト通知
次は投稿にトースト通知を追加していきます。
やりたいこと
静的に作成したフラッシュメッセージですが、stimulus-notification
を使っての動的にしていきたいと思います。
やりたいこと:
- 閉じるボタンをクリックしたらメッセージが消えること
- クリックしない場合3秒後にフェードアウトさせること
stimulus-notification
をインストールする
bin/importmap pin stimulus-notification
Pinning "stimulus-notification" to https://ga.jspm.io/npm:stimulus-notification@2.2.0/dist/stimulus-notification.mjs
Pinning "@hotwired/stimulus" to https://ga.jspm.io/npm:@hotwired/stimulus@3.2.1/dist/stimulus.js
Pinning "hotkeys-js" to https://ga.jspm.io/npm:hotkeys-js@3.11.2/dist/hotkeys.esm.js
Pinning "stimulus-use" to https://ga.jspm.io/npm:stimulus-use@0.51.3/dist/index.js
stimulusコントローラーを作成する
bin/rails g stimulus notification
create app/javascript/controllers/notification_controller.js
import Notification from 'stimulus-notification'
export default class extends Notification {
connect() {
super.connect()
}
}
データ属性を指定する
TailwindCSS
のクラスとアイコンを使ってます。
<% flash.each do |message_type, message| %>
<div class="flex items-center alert-<%= flash_class(message_type) %> ring-1 ring-inset p-4 rounded-md fixed top-20 right-5 transition transform duration-1000" role="alert"
data-controller="notification"
data-notification-delay-value="3000"
data-transition-enter-from="opacity-0 translate-x-6"
data-transition-enter-to="opacity-100 translate-x-0"
data-transition-leave-from="opacity-100 translate-x-0"
data-transition-leave-to="opacity-0 translate-x-6"
>
<%= message %>
<button data-action="notification#hide">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5">
<path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
</svg>
</button>
</div>
<% end %>
</div>
data-notification-delay-value
は通知が消えるまでの遅延(delay)に関する設定になります。3000
は3秒になります。
終わりに
stimulus-components
を使ってたドロップダウンメニューとトースト通知でした。
stimulus-components
とをTailwindCSS
組み合わせたライブラリーも見つけました。
Discussion