1
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 20

Stimulus リファレンス: アクション

Last updated at Posted at 2022-12-19

この記事はGoogle翻訳の結果を編集したものです。

アクションはコントローラーでDOMイベントを処理する方法です。

<div data-controller="gallery">
  <button data-action="click->gallery#next"></button>
</div>
// controllers/gallery_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  next(event) {
    // …
  }
}

アクションは以下の間を接続します。

  • コントローラーメソッド
  • コントローラーの要素
  • DOMイベントリスナー

記述子

data-action値のclick->gallery#nextはアクション記述子と呼ばれます。この記述子では:

  • clickはリッスンするDOMイベントの名前です
  • galleryはコントローラーの識別子です
  • nextは呼び出すメソッドの名前です

イベント速記

Stimulusを使用するとイベント名を省略して上記のボタン/クリックのペアなど、いくつかの一般的な要素/イベントのペアのアクション記述子を短縮できます。

<button data-action="gallery#next"></button>

これらの短縮形のペアの完全なセットは次のとおりです。

要素 デフォルトイベント
a click
button click
details toggle
form submit
input input
input type=submit click
select change
textarea input

キーボードイベントフィルター

特定のキーストロークが使用された場合にのみ、KeyboardEventアクションがコントローラーメソッドを呼び出す必要がある場合があります。

次の例のようにアクション記述子のイベント名に.escを追加することで、Escapeキーのみに応答するイベントリスナーをインストールできます。

<div data-controller="modal"
     data-action="keydown.esc->modal#close" tabindex="0">
</div>

これは、発生するイベントがキーボードイベントである場合にのみ機能します。

これらのフィルターとキーの対応を以下に示します。

フィルター キー
enter Enter
tab Tab
esc Escape
space " "
up ArrowUp
down ArrowDown
left ArrowLeft
right ArrowRight
home Home
end End
[a-z] [a-z]
[0-9] [0-9]

他のキーをサポートする必要がある場合、カスタムスキーマを使用して修飾子をカスタマイズできます。

import { Application, defaultSchema } from "@hotwired/stimulus"

const customSchema = {
  ...defaultSchema,
  keyMappings: { ...defaultSchema.keyMappings, at: "@" },
}

const app = Application.start(document.documentElement, customSchema)

修飾キーを使用して複合フィルターにサブスクライブする場合はctrl+aのように記述できます。

<div data-action="keydown.ctrl+a->listbox#selectAll" role="option" tabindex="0">...</div>

サポートされている修飾キーのリストを以下に示します。

修飾キー 注釈
alt MacOSのoption
ctrl
meta MacOSのコマンドキー
shift

グローバルイベント

コントローラーはグローバルウィンドウまたはドキュメントオブジェクトでディスパッチされたイベントをリッスンする必要がある場合があります。

次の例のようにアクション記述子のイベント名に@windowまたは@documentを(任意のフィルター修飾子と共に)追加して、windowまたはdocumentにそれぞれイベントリスナーをインストールできます。

<div data-controller="gallery"
     data-action="resize@window->gallery#layout">
</div>

オプション

DOMイベントリスナーオプションを指定する必要がある場合、アクション記述子に1つ以上のアクションオプションを追加できます。

<div data-controller="gallery"
     data-action="scroll->gallery#layout:!passive">
  <img data-action="click->gallery#open:capture">

Stimulusは次のアクションオプションをサポートしています。

アクションオプション DOMイベントリスナーオプション
:capture { capture: true }
:once { once: true }
:passive { passive: true }
:!passive { passive: false }

さらに、StimulusはDOMイベントリスナーオプションによってネイティブにサポートされていない次のアクションオプションもサポートしています。

カスタムアクションオプション 説明
:stop メソッドを呼び出す前にイベントで.stopPropagation()を呼び出します
:prevent メソッドを呼び出す前にイベントで.preventDefault()を呼び出します
:self イベントが要素自体によって発生した場合にのみメソッドを呼び出します

Application.registerActionOptionメソッドを使用して独自のアクションオプションを登録できます。

たとえば<details>要素がトグルされるたびにトグルイベントを送出するとします。カスタム:openアクションオプションは要素がトグルされて開いたときにイベントをルーティングするのに役立ちます。

import { Application } from "@hotwired/stimulus"

const application = Application.start()

application.registerActionOption("open", ({ event }) => {
  if (event.type == "toggle") {
    return event.target.open == true
  } else {
    return true
  }
})

同様にカスタム:!openアクションオプションは要素が閉じられたときにイベントをルーティングできます。アクション記述子オプションを!で宣言するプレフィックスはコールバックでfalseに設定された値引数を生成します。

import { Application } from "@hotwired/stimulus"

const application = Application.start()

application.registerActionOption("open", ({ event, value }) => {
  if (event.type == "toggle") {
    return event.target.open == value
  } else {
    return true
  }
})

イベントがコントローラーアクションにルーティングされないようにするにはregisterActionOptionコールバック関数がfalseを返す必要があります。 それ以外の場合、イベントをコントローラーアクションにルーティングするにはtrueを返します。

コールバックは次のキーを持つ単一のオブジェクト引数を受け入れます。

| 名前 | 説明 |
| name | 文字列:オプションの名前(上記の例では"open") |
| value | 真偽値:オプションの値(:opentrueを返し:!openfalseを返します) |
| event | Event: イベントインスタンス |
| element | Element: アクション記述子が宣言されている要素 |

イベント オブジェクト

アクションメソッドはアクションのイベントリスナーとして機能するコントローラー内のメソッドです。

アクションメソッドの最初の引数はDOMイベントオブジェクトです。次のようなさまざまな理由でイベントへのアクセスが必要になる場合があります。

  • キーボードイベントからキーコードを読み取る
  • マウスイベントの座標を読み取る
  • 入力イベントからデータを読み取る
  • アクションサブミッター要素からパラメーターを読み取る
  • イベントに対するブラウザのデフォルトの動作を防止する
  • このアクションにバブルアップする前に、どの要素がイベントをディスパッチしたかを調べる

次の基本プロパティは、すべてのイベントに共通です。

イベントプロパティ
event.type イベントの名前(例:"click"
event.target イベントをディスパッチしたターゲット(つまりクリックされた最も内側の要素)
event.currentTarget イベント リスナーがインストールされているターゲット(data-action属性を持つ要素、またはdocumentまたはwindowのいずれか)
event.params アクションサブミッター要素によって渡されるアクションパラメーター

次のイベントメソッドを使用するとイベントの処理方法をより詳細に制御できます。

イベントメソッド 結果
event.preventDefault() イベントのデフォルトの動作をキャンセルします(リンクをたどる、フォームを送信するなど)
event.stopPropagation() 親要素の他のリスナーにバブルアップする前にイベントを停止します

複数のアクション

data-action属性の値はスペースで区切られたアクション記述子のリストです。

特定の要素に多くのアクションがあることはよくあることです。たとえば次の入力要素はフォーカスを取得するとフィールドコントローラーのhighlight()メソッドを呼び出し、要素の値が変更されるたびにsearchコントローラーのupdate()メソッドを呼び出します。

<input type="text" data-action="focus->field#highlight input->search#update">

要素に同じイベントに対する複数のアクションがある場合、Stimulusは記述子が表示される順序で左から右にアクションを呼び出します。

アクション内でEvent#stopImmediatePropagation()を呼び出すことにより、アクションチェーンをいつでも停止できます。右側への追加のアクションは無視されます。

highlight: function(event) {
  event.stopImmediatePropagation()
  // ...
}

命名規則

アクション名はコントローラーのメソッドに直接マップされるため、アクション名を指定する際は常にcamelCaseを使用してください。

clickonClickhandleClickなど単にイベントの名前を繰り返すアクション名は避けてください。

<button data-action="click->profile#click">Don't</button>

代わりに呼び出されたときに何が起こるかに基づいてアクションメソッドに名前を付けます。

<button data-action="click->profile#showDialog">Do</button>

これによりコントローラーのソースを見なくてもHTMLブロックの動作を理解することができます。

アクションパラメーター

アクションにはサブミッター要素から渡されるパラメーターを含めることができます。data-[identifier]-[param-name]-paramの形式に従います。パラメーターは渡される予定のアクションが宣言されているのと同じ要素で指定する必要があります。

すべてのパラメーターは値から推測されるNumberStringObject、またはBooleanのいずれかに自動的に型キャストされます。

データ属性 パラメーター
data-item-id-param="12345" 12345 Number
data-item-url-param="/votes" "/votes" String
data-item-payload-param='{"value":"1234567"}' { value: 1234567 } Object
data-item-active-param="true" true Boolean

次の設定を検討してください。

<div data-controller="item spinner">
  <button data-action="item#upvote spinner#start" 
    data-item-id-param="12345" 
    data-item-url-param="/votes"
    data-item-payload-param='{"value":"1234567"}' 
    data-item-active-param="true"></button>
</div>

ItemController#upvoteSpinnerController#startの両方を呼び出しますが、パラメーターが渡されるのは前者だけです。

// ItemController
upvote(event) {
  // { id: 12345, url: "/votes", active: true, payload: { value: 1234567 } }
  console.log(event.params) 
}

// SpinnerController
start(event) {
  // {}
  console.log(event.params) 
}

イベントから他に何も必要ない場合はパラメーターを破棄できます。

upvote({ params }) {
  // { id: 12345, url: "/votes", active: true, payload: { value: 1234567 } }
  console.log(params) 
}

または同じコントローラー上の複数のアクションが同じ送信者要素を共有する場合に備えて必要なパラメーターのみを破棄します。

upvote({ params: { id, url } }) {
  console.log(id) // 12345
  console.log(url) // "/votes"
}
1
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
1
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?