自毁的StimulusJS控制器(附实例)

213 阅读3分钟

这篇文章是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 控制器来添加额外的样式,当某些东西被 "选中 "时:

Example of highlighter controller

<template
  data-controller="highlighter"
  data-highlighter-marker-value="<%= dom_id(task, :list_item) %>"
  data-highlighter-highlight-class="text-blue-600 bg-blue-100"></template>

通过使用Stimulusvaluesclasses 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> 。这个控制器无缝地将浏览器的焦点移动到新的输入:

Example of grab-focus controller

// 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在页面之间进行导航时,事情能按预期进行。