Regardless of whether you use declared Reflexes in your HTML markup or call
stimulate() directly from inside of a Stimulus controller, StimulusReflex maps your requests to Reflex classes on the server. These classes are found in
app/reflexes and they inherit from
app/reflexes/example_reflex.rbclass ExampleReflex < ApplicationReflexend
Setting a declarative data-reflex="click->Example#increment" will call the increment Reflex action in the Example Reflex class, before passing any instance variables along to your controller action and re-rendering your page. You can do anything you like in a Reflex action, including database updates, launching ActiveJobs and even initiating CableReady broadcasts.
app/reflexes/example_reflex.rbclass ExampleReflex < ApplicationReflexdef increment@counter += 1 # @counter will be available inside your controller action if you're doing a Page Morphendend
It's very common to want to be able to access the
current_user or equivalent accessor inside your Reflex actions. The best way to achieve this is to delegate it to the ActionCable connection.
app/reflexes/example_reflex.rbclass ExampleReflex < ApplicationReflexdelegate :current_user, to: :connectiondef incrementcurrent_user.counter.increment!endend
If you plan to access
current_user from all of your Reflex classes, it is common to delegate once in your ApplicationReflex.
app/reflexes/application_reflex.rbclass ApplicationReflex < StimulusReflex::Reflexdelegate :current_user, to: :connectionend
The following properties available to the developer inside Reflex actions:
connection - the ActionCable connection
channel - the ActionCable channel
request - an
ActionDispatch::Request proxy for the socket connection
session - the
ActionDispatch::Session store for the current visitor
flash - the
ActionDispatch::Flash::FlashHash for the current request
url - the URL of the page that triggered the reflex
params - an
ActionController::Parameters of the closest form
element - a Hash like object that represents the HTML element that triggered the reflex
reflex_id - a UUIDv4 that uniquely identies each Reflex
element property contains all of the Stimulus controller's DOM element attributes as well as other properties like
value. In addition,
values and the
dataset property reference special collections as described below.
Here's an example that outlines how you can interact with the
element property and the
dataset collection in your Reflex action. You can use the dot notation as well as string and symbol accessors.
app/views/examples/show.html.erb<checkbox id="example" label="Example" checkeddata-reflex="Example#work" data-value="123" />
app/reflexes/example_reflex.rbclass ExampleReflex < ApplicationReflexdef work()element.id # => the HTML element's id in dot notationelement[:id] # => the HTML element's id w/ symbol accessorelement["id"] # => the HTML element's id w/ string accessorelement.dataset # => a Hash that represents the HTML element's datasetelement.values # =>  only for multiple valueselement["id"] # => "example"element[:tag_name] # => "CHECKBOX"element[:checked] # => trueelement.label # => "Example"element["data-reflex"] # => "ExampleReflex#work"element.dataset[:reflex] # => "ExampleReflex#work"element.value # => "123"element["data-value"] # => "123"element.dataset[:value] # => "123"endend
Rails has a pair of cool features that allow developers to generate tokens from ActiveRecord models. These tokens can later be used to access those models, and in the case of signed Global IDs, obscure the model from prying eyes. They can even be set to expire after a period of time.
element accessor on every Reflex has two dynamic accessors,
unsigned which automatically unpack Global IDs stored in data attributes and converts them to model instances.
<div data-reflex="click->Example#foo"data-public="<%= @foo.to_global_id.to_s %>"data-secure="<%= @foo.to_sgid.to_s %>">
While in reality, you'd never use both on the same object, you can now have StimulusReflex automatically convert these attributes into instances of the models they reference. This happens lazily, at the time you access the accessor:
class ExampleReflex < ApplicationReflexdef fooputs element.unsigned[:public] # returns Foo model instanceputs element.signed[:secure] # returns Foo model instanceendend
While most developers default to using signed Global IDs, understand that the tradeoff is that signed tokens can be quite long, whereas unsigned tokens remain short.
If you'd like to wire up 3rd-party exception handling services like Sentry or HoneyBadger to your Reflex classes, you can use
rescue_from to respond to an errors raised.
class MyTestReflex < ApplicationReflexrescue_from StandardError do |exception|ExceptionTrackingService.error(exception)end# ...end
Every Reflex starts as a client-side data structure that is assigned a unique UUIDv4 used to track it through its round-trip life-cycle. Most developers using StimulusReflex never have to think about these details. However, if you're building an application that is based on transactional concepts, it might be very useful to be able to track interactions based on the
class ExampleReflex < ApplicationReflexdef fooputs reflex_idendend