Cheat Sheet速查表
跳转到主要内容

StimulusReflex 速查表

本文根据 Devhints / StimulusReflex 翻译。代码示例及版本范围沿用原文。原文标注更新日期:2021-01-07。

通过数据属性 (Data Attributes)

无需编写任何 JavaScript,即可通过 data-reflex 属性触发 Reflex。

index.html.erb

<a
  href="#"
  data-reflex="click->CounterReflex#increment"
  data-step="1"
  data-count="<%= @count.to_i %>"
  >Increment <%= @count.to_i %></a
>

counter_reflex.rb

class CounterReflex < StimulusReflex::Reflex
  def increment
    @count = element.dataset[:count].to_i + element.dataset[:step].to_i
  end
end

来自 Stimulus.js 控制器

在 StimulusReflex 中注册的 Stimulus.js 控制器,可以使用 stimulate 方法来触发Reflex(反射)

index.html.erb

<a href="#"
  data-controller="counter"
  data-action="click->counter#increment"
>Increment <%= @count %></a>

counter_controller.js

import { Controller } from 'stimulus'
import StimulusReflex from 'stimulus_reflex'

export default class extends Controller {
  connect() {
    StimulusReflex.register(this)
  }

  increment(event) {
    event.preventDefault()
    this.stimulate('Counter#increment', 1)
  }
}

counter_reflex.rb

class CounterReflex < StimulusReflex::Reflex
  def increment(step = 1)
    session[:count] = session[:count].to_i + step
   end
end

多态关联

选择器式更新 (Selector morphs)

无需刷新整个页面,您可以指定页面中的特定部分进行更新。 morph(selector, content)

<!-- show.html.erb -->
<header data-reflex="click->Example#change">
  <%= render partial: "path/to/foo", locals: {message: "Am I the medium or the massage?"} %>
</header>
<!-- _foo.html.erb -->
<div id="foo">
  <span class="spa"><%= message %></span>
</div>
# example_reflex.rb
class ExampleReflex < ApplicationReflex
  def change
    morph "#foo", "Your muscles... they are so tight."
  end
end

“无更新”模式 (Nothing morph)

使用 morph :nothing 用于在服务器端执行操作,但不更新客户端的 Reflex。

# example_reflex.rb
class ExampleReflex < ApplicationReflex
  def change
    LongRunningJob.perform_later
    morph :nothing
  end
end

生命周期

服务器端回调

Reflex 类可以使用以下回调。 完整文档

  • before_reflex
  • around_reflex
  • after_reflex

客户端回调(通用)

StimulusReflex 控制器自动支持五种通用的生命周期回调方法。

  • beforeReflex(element, reflex, noop, reflexId) 在通过 WebSocket 发送请求之前
  • reflexSuccess(element, reflex, noop, reflexId) 在服务器端 Reflex 执行成功且 DOM 已完成更新之后
  • reflexError(element, reflex, error, reflexId) 当服务器端 Reflex 抛出错误时
  • reflexHalted(element, reflex, noop, reflexId) 在 `before_reflex` 回调中使用 `throw :abort` 取消 Reflex 时
  • afterReflex(element, reflex, noop, reflexId) 在执行成功或发生错误之后(无论结果如何)
  • finalizeReflex(element, reflex, noop, reflexId) 在执行成功或发生错误之后(无论结果如何)

客户端回调(自定义)

对于每个 Reflex 动作,StimulusReflex 控制器最多可以定义五个自定义生命周期回调方法。这些方法遵循基于 Reflex 名称的命名约定。例如,对于 e.g: add_one Reflex:

  • beforeAddOne(element, reflex, noop, reflexId)
  • addOneSuccess(element, reflex, noop, reflexId)
  • addOneError(element, reflex, error, reflexId)
  • addOneHalted(element, reflex, noop, reflexId)
  • afterAddOne(element, reflex, noop, reflexId)
  • finalizeAddOne(element, reflex, noop, reflexId)

客户端事件

如果您需要获知某个 Reflex 方法何时被调用,但操作是在发起该调用的 Stimulus 控制器之外进行的,则可以订阅并接收 DOM 事件。

  • stimulus-reflex:before
  • stimulus-reflex:success
  • stimulus-reflex:error
  • stimulus-reflex:halted
  • stimulus-reflex:after

此外,还有与 StimulusReflex 库设置及连接至 ActionCable 相关的事件。

  • stimulus-reflex:connected
  • stimulus-reflex:disconnected
  • stimulus-reflex:rejected
  • stimulus-reflex:ready

实用提示

表单

如果在表单元素(或该表单元素的子元素)上调用 Reflex,则整个表单的数据会被正确序列化,并作为 Reflex 动作方法中的 params 访问器(accessor)提供给该方法。 阅读更多

Promise 异步处理

stimulate() 方法会返回一个 Promise 对象。

this.stimulate('Comments#create')
  .then(() => this.doSomething())
  .catch(() => this.handleError())

从父元素继承 data 属性

你可以使用 data-reflex-dataset="combined" 指令来收集 DOM 层级结构中所有的 data 属性,并将它们作为 Reflex 负载(payload)的一部分进行传递。

<!-- new.html.erb -->
<div data-post-id="<%= @post.id %>">
  <div data-category-id="<%= @category.id %>">
    <button data-reflex="click->Comment#create" data-reflex-dataset="combined">Create</button>
  </div>
</div>
# comment_reflex.rb
class CommentReflex < ApplicationReflex
  def create
    puts element.dataset["post-id"]
    puts element.dataset["category-id"]
  end
end

Reflex 根节点

无需更新整个页面,你可以通过指定 DOM 中的具体部分来决定哪些内容需要更新。 data-reflex-root 属性触发 Reflex。 完整文档

<!-- index.html.erb -->
<div data-reflex-root="[forward],[backward]">
  <input type="text" value="<%= @words %>" data-reflex="keyup->Example#words">
  <div forward><%= @words %></div>
  <div backward><%= @words&.reverse %></div>
</div>
# example_reflex.rb
  def words
    @words = element[:value]
  end

持久化元素

在 DOM 中的任何元素上添加 `data-reflex-permanent` 属性,该元素就不会受到全页面 Reflex 更新或重新渲染局部视图(partials)的 morph 调用影响,从而保持不变。

<!-- index.html.erb -->
<div data-reflex-permanent>
  <iframe src="https://ghbtns.com/github-btn.html?user=hopsoft&repo=stimulus_reflex&type=star&count=true" frameborder="0" scrolling="0" class="ghbtn"></iframe>
  <iframe src="https://ghbtns.com/github-btn.html?user=hopsoft&repo=stimulus_reflex&type=fork&count=true" frameborder="0" scrolling="0" class="ghbtn"></iframe>
</div>

中止 Reflex

调用 raise :abort 在 Reflex 方法内部调用此操作即可将其取消。

# comment_reflex.rb
class CommentReflex < ApplicationReflex
  def create
    raise :abort
  end
end