From c1f9f6f9510b5dca02f5af6b64d7b33db3e8e66c Mon Sep 17 00:00:00 2001 From: Rafe Rosen Date: Thu, 7 Jan 2021 06:40:32 -0500 Subject: [PATCH] Create stimulus_reflex.md (#1576) * Create stimulus_reflex.md * Update and rename stimulus_reflex.md to stimulus-reflex.md Co-authored-by: Rico Sta. Cruz --- stimulus-reflex.md | 244 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 stimulus-reflex.md diff --git a/stimulus-reflex.md b/stimulus-reflex.md new file mode 100644 index 000000000..a1abdc9c7 --- /dev/null +++ b/stimulus-reflex.md @@ -0,0 +1,244 @@ +--- +title: StimulusReflex +category: Ruby +layout: 2017/sheet +updated: 2021-01-07 +--- + +### via Data Attributes + +Trigger reflexes without writing any javascript with the `data-reflex` attribute. + +```erb + +Increment <%= @count.to_i %> +``` + +```ruby +# counter_reflex.rb +class CounterReflex < StimulusReflex::Reflex + def increment + @count = element.dataset[:count].to_i + element.dataset[:step].to_i + end +end +``` + +### from Stimulus.js Controller + +Stimulus.js controllers registered with StimulusReflex can use the `stimulate` method to trigger reflexes + +```erb + +Increment <%= @count %> +``` + +```javascript +// 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) + } +} +``` + +```ruby +# counter_reflex.rb +class CounterReflex < StimulusReflex::Reflex + def increment(step = 1) + session[:count] = session[:count].to_i + step + end +end +``` + +## Morphs + +### Selector morphs + +Instead of refreshing the entire page, you can specify a portion of the page to update with `morph(selector, content)` + +```erb + +
+ <%= render partial: "path/to/foo", locals: {message: "Am I the medium or the massage?"} %> +
+``` + +```erb + +
+ <%= message %> +
+``` + +```ruby +# example_reflex.rb +class ExampleReflex < ApplicationReflex + def change + morph "#foo", "Your muscles... they are so tight." + end +end +``` + +### Nothing morph + +Use `morph :nothing` in reflexes that do something on the server without updating the client. + +```ruby +# example_reflex.rb +class ExampleReflex < ApplicationReflex + def change + LongRunningJob.perform_later + morph :nothing + end +end +``` + +## Lifecycle + +### Server-side callbacks + +Reflex classes can use the following callbacks. [Full Docs](http://docs.stimulusreflex.com/lifecycle#server-side-reflex-callbacks) + +- `before_reflex` +- `around_reflex` +- `after_reflex` + +### Client-side callbacks (generic) + +StimulusReflex controllers automatically support five generic lifecycle callback methods. + +- `beforeReflex(element, reflex, noop, reflexId)` prior to sending a request over the web socket +- `reflexSuccess(element, reflex, noop, reflexId)` after the server side Reflex succeeds and the DOM has been updated +- `reflexError(element, reflex, error, reflexId)` whenever the server side Reflex raises an error +- `reflexHalted(element, reflex, noop, reflexId)` reflex canceled with throw :abort in the before_reflex callback +- `afterReflex(element, reflex, noop, reflexId)` after both success and error +- `finalizeReflex(element, reflex, noop, reflexId)` after both success and error + +### Client-side callbacks (custom) + +StimulusReflex controllers can define up to five custom lifecycle callback methods for each Reflex action. These methods use a naming convention based on the name of the Reflex. e.g. for the `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)` + +### Client-side events + +If you need to know when a Reflex method is called, but you're working outside of the Stimulus controller that initiated it, you can subscribe to receive DOM events + +- `stimulus-reflex:before` +- `stimulus-reflex:success` +- `stimulus-reflex:error` +- `stimulus-reflex:halted` +- `stimulus-reflex:after` + +There are also events related to the StimulusReflex library setting up and connecting to ActionCable + +- `stimulus-reflex:connected` +- `stimulus-reflex:disconnected` +- `stimulus-reflex:rejected` +- `stimulus-reflex:ready` + +## Helpful tips + +### Forms + +If a Reflex is called on a form element - or a child of that form element - then the data for the whole form will be properly serialized and made available to the Reflex action method as the `params` accessor. [Read more](http://docs.stimulusreflex.com/working-with-forms) + +### Promises + +`stimulate()` method returns a promise + +```javascript +this.stimulate('Comments#create') + .then(() => this.doSomething()) + .catch(() => this.handleError()) +``` + +### Inheriting data-attributes from parent elements + +You can use the `data-reflex-dataset="combined"` directive to scoop all data attributes up the DOM hierarchy and pass them as part of the Reflex payload. + +```erb + +
+
+ +
+
+``` + +```ruby +# comment_reflex.rb +class CommentReflex < ApplicationReflex + def create + puts element.dataset["post-id"] + puts element.dataset["category-id"] + end +end +``` + +### Reflex root + +Instead of updating your entire page, you can specify exactly which parts of the DOM will be updated using the `data-reflex-root` attribute. [Full docs](http://docs.stimulusreflex.com/morph-modes#scoping-page-morphs) + +```text + +
+ +
<%= @words %>
+
<%= @words&.reverse %>
+
+``` + +```ruby +# example_reflex.rb + def words + @words = element[:value] + end +``` + +### Permanent elements + +Add data-reflex-permanent to any element in your DOM, and it will be left unchanged by full-page Reflex updates and morph calls that re-render partials. + +```erb + +
+ + +
+``` + +### Aborting a reflex + +call `raise :abort` within a reflex method to cancel it. + +```ruby +# comment_reflex.rb +class CommentReflex < ApplicationReflex + def create + raise :abort + end +end +```