跳至内容

操作

操作是您在控制器中处理 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) {
// …
}
}

操作是以下内容之间的连接

描述符

data-actionclick->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 筛选器

在某些情况下,KeyboardEvent 操作仅应在使用特定击键时调用控制器方法。

您可以安装一个仅响应 Escape 键的事件侦听器,方法是在操作描述符的事件名称中添加 .esc,如下例所示。

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

</div>

这仅在触发的事件是键盘事件时才有效。

以下显示了这些筛选器和键之间的对应关系。

过滤器 键名
enter 回车
tab 制表符
esc 退出
space " "
向上 向上箭头
向下 向下箭头
向左 向左箭头
向右 向右箭头
home 主页
end 结束
page_up 向上翻页
page_down 向下翻页
[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 上的 Command 键
shift

全局事件

有时控制器需要监听在全局 windowdocument 对象上派发的事件。

您可以在操作描述符中将 @window@document 附加到事件名称(以及任何过滤器修饰符),以分别在 windowdocument 上安装事件侦听器,如下例所示

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

</div>

选项

如果您需要指定 DOM 事件侦听器选项,您可以将一个或多个操作选项附加到操作描述符中。

<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 操作选项可以在元素切换为关闭时路由事件。使用 ! 前缀声明操作描述符选项将在回调中生成一个 value 参数,该参数设置为 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 布尔值:选项的值(:open 将生成 true:!open 将生成 false
event 事件:事件实例,包括提交者元素上的 params 操作参数
element 元素:声明操作描述符的元素
controller 将接收方法调用的 Controller 实例

事件对象

操作方法是控制器中作为操作事件侦听器的函数。

操作方法的第一个参数是 DOM 事件对象。你可能出于多种原因需要访问事件,包括

所有事件都有以下基本属性

事件属性
event.type 事件的名称(例如 "click"
event.target 分派事件的目标(即被点击的最内层元素)
event.currentTarget 安装事件侦听器的目标(具有 data-action 属性的元素,或 documentwindow
event.params 操作提交者元素传递的操作参数


以下事件方法让你可以更好地控制事件的处理方式

事件方法 结果
event.preventDefault() 取消事件的默认行为(例如,跟随链接或提交表单)
event.stopPropagation() 阻止事件冒泡到父元素上的其他监听器

多个操作

data-action 属性的值是操作描述符的空格分隔列表。

任何给定元素通常都有很多操作。例如,当获得焦点时,以下输入元素调用 field 控制器 的 highlight() 方法,并且在元素值每次更改时调用 search 控制器 的 update() 方法

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

当元素针对同一事件有多个操作时,Stimulus 会按描述符出现的顺序从左到右调用这些操作。

可以在任何时候通过在操作中调用 Event#stopImmediatePropagation() 来停止操作链。右侧的任何其他操作都将被忽略

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

命名约定

始终使用驼峰式大小写来指定操作名称,因为它们直接映射到控制器上的方法。

避免使用简单重复事件名称的操作名称,例如 clickonClickhandleClick

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

相反,根据调用操作时将发生的情况来命名操作方法

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

这将帮助你推理 HTML 块的行为,而无需查看控制器源。

操作参数

操作可以具有从提交元素传递的参数。它们遵循 data-[identifier]-[param-name]-param 的格式。必须在声明要将参数传递到的操作所在的元素上指定参数。

所有参数都自动类型转换为 NumberStringObjectBoolean,由其值推断

数据属性 参数 类型
data-item-id-param="12345" 12345 数字
data-item-url-param="/votes" "/votes" 字符串
data-item-payload-param='{"value":"1234567"}' { value: 1234567 } 对象
data-item-active-param="true" true 布尔值


考虑此设置

<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"
}

下一步:目标