この記事はGoogle翻訳の結果を編集したものです。
コントローラーはStimulusアプリケーションの基本的な組織単位です。
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
// …
}
コントローラーはアプリケーションで定義するJavaScriptクラスのインスタンスです。各コントローラークラスは@hotwired/stimulus
モジュールによってエクスポートされたContoller
基底クラスを継承します。
プロパティ
すべてのコントローラーはStimulusアプリケーションインスタンスに属し、HTML要素に関連付けられています。 コントローラークラス内ではコントローラーの以下にアクセスできます。
-
this.application
プロパティを介したアプリケーション -
this.element
プロパティを介したHTML要素 -
this.identifier
プロパティを介した識別子
モジュール
ファイルごとに1つずつJavaScriptモジュールでコントローラークラスを定義します。上記の例のように各コントローラークラスをモジュールのデフォルトオブジェクトとしてエクスポートします。
これらのモジュールをcontrollers/
ディレクトリに配置します。ファイルに[identifier]_controller.js
という名前を付けます。[identifier]
は各コントローラーの識別子に対応します。
識別子
識別子はHTMLでコントローラークラスを参照するために使用する名前です。
data-controller
属性を要素に追加すると、Stimulusは属性の値から識別子を読み取り、対応するコントローラークラスの新しいインスタンスを作成します。
たとえば、この要素にはcontrollers/reference_controller.js
で定義されたクラスのインスタンスであるコントローラーがあります。
<div data-controller="reference"></div>
以下はStimulusがそのコンテキストでコントローラーの識別子を生成する方法の例です。
コントローラーファイルの名前が... | その識別子は... |
---|---|
clipboard_controller.js | clipboard |
date_picker_controller.js | date-picker |
users/list_item_controller.js | users--list-item |
local-time-controller.js | local-time |
スコープ
Stimulusがコントローラーを要素に接続すると、その要素とそのすべての子がコントローラーのスコープを構成します。
たとえば以下の<div>
と<h1>
はコントローラーのスコープの一部ですが、まわりの<main>
要素はそうではありません。
<main>
<div data-controller="reference">
<h1>Reference</h1>
</div>
</main>
ネストされたスコープ
ネストされている場合、各コントローラーはネストされているコントローラーのスコープを除いて、独自のスコープのみを認識します。
たとえば以下の#parent
コントローラーは、そのスコープ内のitem
ターゲットのみを認識し、#child
コントローラーのターゲットは認識しません。
<ul id="parent" data-controller="list">
<li data-list-target="item">One</li>
<li data-list-target="item">Two</li>
<li>
<ul id="child" data-controller="list">
<li data-list-target="item">I am</li>
<li data-list-target="item">a nested list</li>
</ul>
</li>
</ul>
複数のコントローラー
data-controller
属性の値はスペースで区切られた識別子のリストです。
<div data-controller="clipboard list-item"></div>
ページ上の特定の要素に多くのコントローラーがあるのはよくあることです。上記の例では<div>
にclipboard
とlist-item
の2つのコントローラーが接続されています。
同様にページ上の複数の要素が同じコントローラークラスを参照することもよくあります。
<ul>
<li data-controller="list-item">One</li>
<li data-controller="list-item">Two</li>
<li data-controller="list-item">Three</li>
</ul>
それぞれの<li>
にはlist-item
コントローラーの独自のインスタンスがあります。
命名規則
コントローラークラスのメソッド名とプロパティ名には常にcamelCaseを使用します。
識別子が複数の単語で構成されている場合は単語をkebab-caseで記述します(つまりダッシュを使用して: date-picker
、list-item
)。
ファイル名ではアンダースコアまたはダッシュを使用して複数の単語を区切ります(snake_caseまたはkebab-case: controllers/date_picker_controller.js、controllers/list-item-controller.js
)。
登録
Stimulus for Railsを@hotwired/stimulus-webpack-helpers
パッケージと一緒にインポートマップまたはWebpackと共に使用する場合、アプリケーションは上記の規則に従ってコントローラークラスを自動的に読み込み登録します。
そうでない場合、アプリケーションは各コントローラークラスを手動でロードして登録する必要があります。
コントローラーを手動で登録する
識別子を使用してコントローラークラスを手動で登録するには、まずクラスをインポートしてからアプリケーションオブジェクトでApplication#register
メソッドを呼び出します。
import ReferenceController from "./controllers/reference_controller"
application.register("reference", ReferenceController)
モジュールからインポートする代わりにコントローラークラスをインラインで登録することもできます。
import { Controller } from "@hotwired/stimulus"
application.register("reference", class extends Controller {
// …
})
環境要因に基づく登録の防止
特定の環境要因(特定のユーザー エージェントなど)が満たされた場合にのみコントローラーを登録してロードする場合は、静的shouldLoad
メソッドを上書きできます。
class UnloadableController extends ApplicationController {
static get shouldLoad() {
return false
}
}
// This controller will not be loaded
application.register("unloadable", UnloadableController)
コントローラー登録時のトリガー動作
コントローラーが登録された後に何らかの動作をトリガーしたい場合、静的なafterLoad
メソッドを追加できます。
class SpinnerButton extends Controller {
static afterLoad(identifier, application) {
// use the application instance to read the configured 'data-controller' attribute
const { controllerAttribute } = application.schema
// update any legacy buttons with the controller's registered identifier
const updateLegacySpinners = () => {
document.querySelector(".legacy-spinner-button").forEach((element) => {
element.setAttribute(controllerAttribute, identifier)
})
}
// called as soon as registered so DOM may not have loaded yet
if (document.readyState == "loading") {
document.addEventListener("DOMContentLoaded", updateLegacySpinners)
} else {
updateLegacySpinners()
}
}
}
// This controller will update any legacy spinner buttons to use the controller
application.register("spinner-button", SpinnerButton)
afterLoad
メソッドはDOMに制御対象の要素が存在しない場合でも、コントローラーが登録されるとすぐに呼び出されます。 コントローラーとStimulusアプリケーションインスタンスを登録するときに使用された識別子で呼び出されます。
イベントとのクロスコントローラー調整
コントローラーが相互に通信する必要がある場合はイベントを使用する必要があります。Controller
クラスにはこれを簡単にするdispatch
と呼ばれる便利なメソッドがあります。最初の引数としてeventName
を取り、コロンで区切られたコントローラーの名前が自動的に前に付けられます。ペイロードは詳細に保持されます。それはこのように動作します:
class ClipboardController extends Controller {
static targets = [ "source" ]
copy() {
this.dispatch("copy", { detail: { content: this.sourceTarget.value } })
navigator.clipboard.writeText(this.sourceTarget.value)
}
}
そして、このイベントを別のコントローラーのアクションにルーティングできます。
<div data-controller="clipboard effects" data-action="clipboard:copy->effects#flash">
PIN: <input data-clipboard-target="source" type="text" value="1234" readonly>
<button data-action="clipboard#copy">Copy to Clipboard</button>
</div>
dispatch
は次のように2番目のパラメーターとして追加のオプションを受け入れます。
オプション | デフォルト | 注釈 |
---|---|---|
detail |
{} 空のオブジェクト |
CustomEvent.detailを参照 |
target |
this.element |
Event.targetを参照 |
prefix |
this.identifier |
プレフィックスがfalse の場合(null またはfalse など)、eventName のみが使用されます 文字列値を指定すると指定された文字列とコロンがeventName の先頭に追加されます。 |
bubbles |
true |
Event.bubblesを参照 |
cancelable |
true |
Event.cancelableを参照 |
dispatch
は生成されたCustomEvent
を返します。これを使用して次のように他のリスナーによってイベントをキャンセルする方法を提供できます。
class ClipboardController extends Controller {
static targets = [ "source" ]
copy() {
const event = this.dispatch("copy", { cancelable: true })
if (event.defaultPrevented) return
navigator.clipboard.writeText(this.sourceTarget.value)
}
}
class EffectsController extends Controller {
flash(event) {
// this will prevent the default behaviour as determined by the dispatched event
event.preventDefault()
}
}
他のコントローラを直接呼び出す
何らかの理由でイベントを使用してコントローラー間で通信できない場合は、アプリケーションからgetControllerForElementAndIdentifier
メソッドを介してコントローラーインスタンスにアクセスできます。 これはイベントを使用するより一般的な方法では解決できない固有の問題がある場合にのみ使用する必要がありますが、必要な場合は次のようにします。
class MyController extends Controller {
static targets = [ "other" ]
copy() {
const otherController = this.application.getControllerForElementAndIdentifier(this.otherTarget, 'other')
otherController.otherMethod()
}
}