本文根据 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_reflexaround_reflexafter_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:beforestimulus-reflex:successstimulus-reflex:errorstimulus-reflex:haltedstimulus-reflex:after
此外,还有与 StimulusReflex 库设置及连接至 ActionCable 相关的事件。
stimulus-reflex:connectedstimulus-reflex:disconnectedstimulus-reflex:rejectedstimulus-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