操作
操作是您在控制器中处理 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 筛选器
在某些情况下,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 |
﹟ 全局事件
有时控制器需要监听在全局 window
或 document
对象上派发的事件。
您可以在操作描述符中将 @window
或 @document
附加到事件名称(以及任何过滤器修饰符),以分别在 window
或 document
上安装事件侦听器,如下例所示
<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 属性的元素,或 document 或 window ) |
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()
// ...
}
﹟ 命名约定
始终使用驼峰式大小写来指定操作名称,因为它们直接映射到控制器上的方法。
避免使用简单重复事件名称的操作名称,例如 click
、onClick
或 handleClick
<button data-action="click->profile#click">Don't</button>
相反,根据调用操作时将发生的情况来命名操作方法
<button data-action="click->profile#showDialog">Do</button>
这将帮助你推理 HTML 块的行为,而无需查看控制器源。
﹟ 操作参数
操作可以具有从提交元素传递的参数。它们遵循 data-[identifier]-[param-name]-param
的格式。必须在声明要将参数传递到的操作所在的元素上指定参数。
所有参数都自动类型转换为 Number
、String
、Object
或 Boolean
,由其值推断
数据属性 | 参数 | 类型 |
---|---|---|
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#upvote
和 SpinnerController#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"
}