Stimulusで知っておきたい10の機能(翻訳)
Stimulusは、今あるHTMLで使える控えめなJavaScriptフレームワークとして喧伝されていますが、Stimulusにはおなじみの機能の他にも、あなたの知らない機能や忘れてしまった機能がいくつも詰まっています。
🔗 1: 存在確認用のプロパティ
StimulusのAPI(ターゲット、CSSクラス、値、アウトレット)には、それぞれ存在確認用の属性オプションが必ずあります。つまり、ある属性が利用可能かどうかを以下のようにチェックできます。
hasButtonTarget
(ターゲットがButton
の場合)hasButtonClass
(CSSクラス名がButton
の場合)hasLabelValue
(値がLabel
の場合)hasActionsOutlet
(アウトレットがAction
の場合)
これらの属性のブール値(true
またはfalse
を返す)に基づいて、以下のようにロジックを実行できます。
update() {
if (!this.hasButtonTarget) return;
this.buttonTarget.classList.add(this.hasButtonClass ? this.buttonClass : "btn");
}
🔗 2: ターゲットでconnected
コールバックやdisconnected
コールバックが使える
Stimulusクラスで使えるライフサイクルメソッドであるconnected
やdisconnected
については、ほとんどの方がご存知でしょう。しかし、[name]TargetConnected()
メソッドや[name]TargetDisconnected()
メソッドもあるのです([name]
はターゲット名のプレースホルダ)。これらは、ターゲットが接続したタイミングや切断したタイミングで呼び出されます。
レスポンスにTurbo Streamsを使う場合、このコールバックには多くの使い道があります。
- 検索結果の数値を表示するカウンタを更新する
- 人物のリストに動的に追加されたときに(アルファベット順で)並べ替える
- 接続したターゲットに応じて読み込み状態を表示/非表示にする
以下のような感じで使います。
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["item", "count"];
// ...
itemTargetConnected() {
this.#updateItemCount();
}
itemTargetDisconnected() {
this.#updateItemCount();
}
// private
#updateItemCount() {
this.countTarget.textContent = `(${this.itemTargets.length})`;
}
}
[name]TargetDisconnected()
メソッドは、ライフサイクルメソッドであるdisconnect()
が呼び出される前に実行される点にご注意ください。
Stimulusのconnected
コールバックやdisconnected
コールバックについてもっと詳しく知りたい方向けに、以下の記事も書きました。
参考: Connected and Disconnected Target Callbacks with Stimulus | Rails Designer
🔗 3: Stimulusのアクションにパラメータを渡す
Stimulusのメソッドに何らかの属性を引数として渡す必要が生じることがあります。これは以下のように簡単に行えます。
<div data-controller="theme">
<button data-action="theme#update" data-theme-value-param="dark">
Lights Off
</button>
</div>
訳注
data-action="theme#update"
:theme
コントローラのupdate
メソッドを呼び出すdata-theme-value-param="dark"
:theme
コントローラのupdate
メソッドのvalue
パラメータに"dark"
を渡す
続いて以下のようにupdate
メソッドを書きます。
update({ params: { value } }) {
this.#setClass(value);
}
渡すパラメータ名はdata-識別子-パラメータ名-param
という構造に従います。パラメータには、StringやNumberからObjectやBooleanに至るまで、任意の型の値を渡せます。詳しくはStimulusのアクションのパラメータを参照してください。
🔗 4: :prevent
オプションでデフォルトの振る舞いをキャンセルする
アクションではさまざまなオプションを利用できますが、:prevent
オプションもその1つです。皆さんはおそらくメソッドでevent.preventDefault()
を呼び出したことがあるかと思いますが、Stimulusはそのショートカットとして:prevent
オプションを提供しています。以下の例を見てみましょう。
<input
type="text"
data-controller="input"
data-action="keypress->input#validate:prevent"
placeholder="Numbers only"
>
export default class extends Controller {
validate(event) {
if (/[0-9]/.test(event.key)) {
event.target.value += event.key
}
}
}
:prevent
オプション(preventDefault()
)を指定しないと、バリデーションチェックの後でも無効な文字(非数値)が入力に表示されたままになってしまいます。
🔗 5: :stop
オプションでプロパゲーションを止める
Stimulusコントローラのアクションやメソッド内で使うevent.stopPropagation()
には、:stop
というショートカットもあります。:stop
オプションを指定すると、イベントがDOM内を駆け上がらないようになります。以下のコード例を見る方がわかりやすいでしょう。
<div data-controller="dropdown" data-action="click@window->dropdown#hide">
<button data-action="dropdown#toggle:stop">
Toggle Dropdown
</button>
<div data-dropdown-target="menu">
<a href="/profile">Profile</a>
<a href="/settings">Settings</a>
<a href="/logout">Logout</a>
</div>
</div>
data-action="dropdown#toggle:stop
の:stop
オプションを指定しないと、このクリックイベントがDOM内を駆け上がって、親要素のdata-action
にあるhide
アクションにまで達してしまいます。これはよくありません。
「イベントが駆け上がること」や「preventDefault
の意味」について何となくはわかっていても、まだ正確にはわからない方は、ぜひ私の新刊書籍を予約してください↓。
参考: JavaScript for Rails Developers
🔗 6: :self
オプション
アクションのカスタムオプションには、:self
オプションもあります。これは、その要素自身の場合にのみ発火し、子要素では発火しないようにします。
<div data-controller="dropdown">
<div data-action="click->dropdown#toggle:self">
Menu Header
<ul class="menu-items">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
</div>
上の例にあるtoggle
メソッドは、Menu Header
をクリックした場合にのみ発火し、.menu-items
(および子要素)をクリックした場合は発火しなくなります。
アクションのカスタムオプションを作ってみたい方は、以下の記事をどうぞ。
参考: Advanced Stimulus: Custom Action Options | Rails Designer
あるいは、私が作ったカスタムのアクションオプションのコレクションであるStimulus-fxもチェックしてみてください。☺️
参考: Stimulus Fx | Rails Designer
🔗 7: CSSクラスを複数追加する
さまざまなCSSクラスを追加・削除したりオンオフしたりしたい場合は、StimulusのクラスAPIがぴったりです。Stimulusは、複数のCSSクラスを一気に追加できます。必要なのはJavaScriptの構文に関するわずかな知識だけです。
以下のようにスプレッド演算子...
を使えばできます。
export default class extends Controller {
static classes = ["scrolling"];
scroll() {
this.element.classList.add(...this.scrollingClasses);
}
}
Stimulusで複数のCSSクラスをオンオフする方法については、以下の記事で詳しく説明しています。
参考: How to Toggle Multiple CSS Classes with Stimulus | Rails Designer
🔗 8: コントローラ要素のネストに注意
Stimulusで使う個別のコントローラは、互いに切り離された形で動作します。つまり、コントローラがアクセスできる対象は、その時点の現在のコンテキストで定義されているターゲットだけなのです。
親のコントローラは、ネストしている子のコントローラにアクセスできませんし、逆に子のコントローラも親のコントローラにはアクセスできません。
タブの中にタブをネストする例をコードで見たい方は以下をどうぞ。
<div data-controller="tabs">
<div data-tabs-target="panel">
<!-- このpanelは、親コントローラから認識できる -->
</div>
<div data-controller="tabs">
<div data-tabs-target="panel">
<!-- このpanelは、ネストしたtabsコントローラからのみ認識できる -->
</div>
</div>
</div>
このことにたまたま気づいた人もいるかもしれませんし、さんざんハマった末に知った人もいるかと思いますが、それでも知っておくとよい機能です。
🔗 9: shouldLoad
メソッド
モバイルデバイスでのみ有効にしたい機能がある場合や、タッチパネルのあるデバイス専用の機能がある場合は、StimulusコントローラでshouldLoad
を使えば以下のように機能を条件付きで読み込めます。
export default class extends Controller {
static get shouldLoad() {
return window.innerWidth <= 768 && "ontouchstart" in window
}
}
このコントローラは、上の条件が一致しない限り登録も読み込みも一切行われないので、コントローラのインスタンスそのものが作成されなくなります。
この振る舞いは、connect
ライフサイクルメソッドにreturn false
を追加するような場合(インスタンスは「作成される」)とは異なることにご注意ください。
🔗 10: afterLoad
メソッド
afterLoad
メソッドは、Stimulusコントローラが登録されたときに、それが要素で使われているかどうか(接続されているかどうか)にかかわらず直ちに何らかのセットアップコードを実行する必要がある場合に最適です。
これもコードスニペットを見る方が早いでしょう。
export default class extends Controller {
static afterLoad(identifier, application) {
// 実行したいことをここに書く
}
}
正直に言うと、afterLoad()
のうまい使い道について長い間考えてきたのですが、これといったものを思いつきません。とはいうものの、前述のshouldLoad()
と使い方は多少似ていますし、知っておくとよいメソッドなのでここに書きました。うまい使い道を見つけた方は、ぜひフォームにてお知らせください!
Stimulusで知っておきたい10の知られざる機能の紹介は、以上でおしまいです。
皆さんはいくつ知っていましたか?
概要
元サイトの許諾を得て翻訳・公開いたします。
日本語タイトルは内容に即したものにしました。
また、APIドキュメントへのリンクも追加しています。