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

:::note
この記事は[Google翻訳](https://translate.google.co.jp/)の結果を編集したものです。
:::

https://stimulus.hotwired.dev/reference/actions

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

```html
<div data-controller="gallery">
  <button data-action="click->gallery#next">…</button>
</div>
```

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

```html
<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](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent)アクションがコントローラーメソッドを呼び出す必要がある場合があります。

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

```html
<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] |

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

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

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

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

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

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

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

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

## グローバルイベント

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

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

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

## オプション

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

```html
<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>`要素がトグルされるたびに[トグル](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDetailsElement/toggle_event)イベントを送出するとします。カスタム`:open`アクションオプションは要素がトグルされて開いたときにイベントをルーティングするのに役立ちます。

```javascript
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`に設定された値引数を生成します。

```javascript
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 | 真偽値：オプションの値（`:open`は`true`を返し`:!open`は`false`を返します） |
| event | [Event](https://developer.mozilla.org/en-US/docs/web/api/event): イベントインスタンス |
| element | [Element](https://developer.mozilla.org/en-US/docs/Web/API/element): アクション記述子が宣言されている要素 |

# イベント オブジェクト

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

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

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

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

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

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

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

# 複数のアクション

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

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

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

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

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

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

# 命名規則

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

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

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

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

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

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

# アクションパラメーター

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

すべてのパラメーターは値から推測される`Number`、`String`、`Object`、または`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 |

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

```html
<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#upvote`と`SpinnerController#start`の両方を呼び出しますが、パラメーターが渡されるのは前者だけです。

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

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

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

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

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

```javascript
upvote({ params: { id, url } }) {
  console.log(id) // 12345
  console.log(url) // "/votes"
}
```