这篇文章是Hotwire Summer的一部分:Boring Rails的新一季内容!
有时候,你需要撒上一点JavaScript来做一个小小的用户体验改进。在过去的日子里,全栈开发人员经常会把小的jQuery片段直接放到页面中:
<script type="application/javascript">
$(".flash-container").delay(5000).fadeOut()
$(".items").last().highlight()
</script>
它完成了工作,但不是最好的。
在Hotwire应用程序中,你可以使用一个 "自毁 "的Stimulus控制器来达到同样的效果。
自毁?
自毁的Stimulus控制器运行一段代码,然后通过调用this.element.remove() ,将自己从DOM中移除。
让我们看一个例子:
// app/javascript/controllers/scroll_to_controller.js
import { Controller } from '@hotwired/stimulus'
export default class extends Controller {
static values = {
location: String
}
connect() {
this.targetElement.scrollIntoView()
this.element.remove()
}
get targetElement() {
return document.getElementById(this.locationValue)
}
}
这个控制器接收了一个location 值,然后滚动页面来显示该元素:
<template
data-controller="scroll-to"
data-scroll-to-location-value="task_12345"></template>
对于自毁的控制器,我喜欢使用<template> 标签,因为它不会在浏览器中显示,而且在阅读代码时是一个很好的信号,表明这不是一个空的div 。
这种模式在Turbo Stream响应中效果非常好。
想象一下,你有一个任务列表,其中有一个创建新任务的内联表单。你可以提交表单,然后发回一个<turbo-stream> ,将其追加到列表中,然后滚动页面到新创建的任务:
<%= turbo_stream.append :tasks, @task %>
<%= turbo_stream.append :tasks do %>
<template
data-controller="scroll-to"
data-scroll-to-location-value="<% dom_id(@task) %>"></template>
<% end %>
因为我们在Stimulus控制器中封装了我们的一小部分JavaScript功能,所有的生命周期事件都被处理了。不需要监听turbo:load ,它只是工作。
你还可以用它来做什么?
荧光笔
我们使用这个highlighter 控制器来添加额外的样式,当某些东西被 "选中 "时:

<template
data-controller="highlighter"
data-highlighter-marker-value="<%= dom_id(task, :list_item) %>"
data-highlighter-highlight-class="text-blue-600 bg-blue-100"></template>
通过使用Stimulusvalues 和classes API,这个控制器是超级可重用的:我们可以指定任何DOM元素的id和任何我们想用来突出元素的类:
// app/javascript/controllers/highlighter_controller.js
import { Controller } from '@hotwired/stimulus'
export default class extends Controller {
static values = {
marker: String
}
static classes = ["highlight"]
connect() {
this.markedElement.classList.add(...this.highlightClasses)
this.element.remove()
}
get markedElement() {
return document.getElementById(this.markerValue)
}
}
抓住焦点
我们用一个grab-focus 控制器来做一个可以快速添加任务的表单。提交表单可以创建任务,然后为下一个任务动态地添加一个新的<form> 。这个控制器无缝地将浏览器的焦点移动到新的输入:

// app/javascript/controllers/grab_focus_controller.js
import { Controller } from '@hotwired/stimulus'
export default class extends Controller {
static values = {
selector: String
}
connect() {
this.grabFocus()
this.element.remove()
}
grabFocus() {
if (this.hasSelectorValue) {
document.querySelector(this.selectorValue)?.focus()
}
}
}
分析 "信标"
我们从HEY那里借用了这个想法,并将其用于跟踪页面分析。我们在页面上添加了一个beacon ,通过ping后台来记录页面的浏览量,然后自行删除。
(如果你喜欢,你甚至可以使用Beacon Web API,但为了简单起见,我们在这里只是发送一个PATCH请求!)
// app/javascript/controllers/beacon_controller.js
import { Controller } from '@hotwired/stimulus'
import { patch } from '@rails/request.js'
export default class extends Controller {
static values = { url: String }
connect() {
patch(this.urlValue)
this.element.remove()
}
}
我们用一个Rails视图助手来包装这个API,使其更加简洁:
module AnalyticsHelper
def tracking_beacon(url:)
tag.template data: { controller: "beacon", beacon_url_value: url }
end
end
<!-- Inside app/views/layouts/plan.html.erb -->
<%= tracking_beacon(url: plan_viewings_path(@plan)) %>
把它包起来
自毁的Stimulus控制器是一种很好的方式,可以通过添加一些JavaScript行为来增强Hotwire应用程序,而不必在客户端上完全弹出并建立整个功能。保持它们的小规模和单一用途,你就可以在不同的页面和不同的背景下重复使用它们。
利用现有的Stimulus控制器的生命周期,确保在通过Turbo Streams改变内容和通过Turbo Drive在页面之间进行导航时,事情能按预期进行。