3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Hotwire ハンドブック 日本語訳Advent Calendar 2022

Day 18

Stimulus リファレンス: コントローラー

Last updated at Posted at 2022-12-17

この記事は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>clipboardlist-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-pickerlist-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()
  }
}
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?