permalink | order |
---|---|
/reference/actions.html |
2 |
Actions are how you handle DOM events in your controllers.
<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) {
// …
}
}
An action is a connection between:
- a controller method
- the controller's element
- a DOM event listener
The data-action
value click->gallery#next
is called an action descriptor. In this descriptor:
click
is the name of the DOM event to listen forgallery
is the controller identifiernext
is the name of the method to invoke
Stimulus lets you shorten the action descriptors for some common element/event pairs, such as the button/click pair above, by omitting the event name:
<button data-action="gallery#next">…</button>
The full set of these shorthand pairs is as follows:
Element | Default Event |
---|---|
a | click |
button | click |
details | toggle |
form | submit |
input | input |
input type=submit | click |
select | change |
textarea | input |
You may have Actions that you wish to run only when certain keystrokes are received.
You can install an event listener that responds only to the Escape
key by adding .esc
to the event name of the action descriptor, as in the following example.
<div data-controller="modal"
data-action="keydown.esc->modal#close" tabindex="0">
</div>
This will only work if the event being fired is a keyboard event.
The correspondence between these filter modifier and keys is shown below.
Modifier | Key Name |
---|---|
enter | Enter |
tab | Tab |
esc | Escape |
space | " " |
up | ArrowUp |
down | ArrowDown |
left | ArrowLeft |
right | ArrowRight |
home | Home |
end | End |
If you need to support other keys, you can customize the modifier using custom schema.
import { Application, defaultSchema } from "@hotwired/stimulus"
const customSchema = {
...defaultSchema,
keyMappings: {...defaultSchema.keyMappings, a: "a", w: "w", s: "s", d: "d" },
}
const app = Application.start(document.documentElement, customSchema);
Sometimes a controller needs to listen for events dispatched on the global window
or document
objects.
You can append @window
or @document
to the event name (contains filter modifer) in an action descriptor to install the event listener on window
or document
, respectively, as in the following example:
<div data-controller="gallery"
data-action="resize@window->gallery#layout">
</div>
You can append one or more action options to an action descriptor if you need to specify DOM event listener options.
<div data-controller="gallery"
data-action="scroll->gallery#layout:!passive">
<img data-action="click->gallery#open:capture">
Stimulus supports the following action options:
Action option | DOM event listener option |
---|---|
:capture |
{ capture: true } |
:once |
{ once: true } |
:passive |
{ passive: true } |
:!passive |
{ passive: false } |
An action method is the method in a controller which serves as an action's event listener.
The first argument to an action method is the DOM event object. You may want access to the event for a number of reasons, including:
- to read the key code from a keyboard event
- to read the coordinates of a mouse event
- to read data from an input event
- to read params from the action submitter element
- to prevent the browser's default behavior for an event
- to find out which element dispatched an event before it bubbled up to this action
The following basic properties are common to all events:
Event Property | Value |
---|---|
event.type | The name of the event (e.g. "click" ) |
event.target | The target that dispatched the event (i.e. the innermost element that was clicked) |
event.currentTarget | The target on which the event listener is installed (either the element with the data-action attribute, or document or window ) |
event.params | The action params passed by the action submitter element |
The following event methods give you more control over how events are handled:
Event Method | Result |
---|---|
event.preventDefault() | Cancels the event's default behavior (e.g. following a link or submitting a form) |
event.stopPropagation() | Stops the event before it bubbles up to other listeners on parent elements |
The data-action
attribute's value is a space-separated list of action descriptors.
It's common for any given element to have many actions. For example, the following input element calls a field
controller's highlight()
method when it gains focus, and a search
controller's update()
method every time the element's value changes:
<input type="text" data-action="focus->field#highlight input->search#update">
When an element has more than one action for the same event, Stimulus invokes the actions from left to right in the order that their descriptors appear.
The action chain can be stopped at any point by calling Event#stopImmediatePropagation()
within an action. Any additional actions to the right will be ignored:
highlight: function(event) {
event.stopImmediatePropagation()
// ...
}
Always use camelCase to specify action names, since they map directly to methods on your controller.
Avoid action names that simply repeat the event's name, such as click
, onClick
, or handleClick
:
<button data-action="click->profile#click">Don't</button>
Instead, name your action methods based on what will happen when they're called:
<button data-action="click->profile#showDialog">Do</button>
This will help you reason about the behavior of a block of HTML without having to look at the controller source.
Actions can have parameters that are be passed from the submitter element. They follow the format of data-[identifier]-[param-name]-param
. Parameters must be specified on the same element as the action they intend to be passed to is declared.
All parameters are automatically typecast to either a Number
, String
, Object
, or Boolean
, inferred by their value:
Data attribute | Param | Type |
---|---|---|
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 |
Consider this setup:
<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>
It will call both ItemController#upvote
and SpinnerController#start
, but only the former will have any parameters passed to it:
// ItemController
upvote(event) {
// { id: 12345, url: "/votes", active: true, payload: { value: 1234567 } }
console.log(event.params)
}
// SpinnerController
start(event) {
// {}
console.log(event.params)
}
If we don't need anything else from the event, we can destruct the params:
upvote({ params }) {
// { id: 12345, url: "/votes", active: true, payload: { value: 1234567 } }
console.log(params)
}
Or destruct only the params we need, in case multiple actions on the same controller share the same submitter element:
upvote({ params: { id, url } }) {
console.log(id) // 12345
console.log(url) // "/votes"
}