# StimulusReflex cheatsheet
The one-page guide to StimulusReflex: usage, examples, links, snippets, and more.

### via Data Attributes

Trigger reflexes without writing any javascript with the `data-reflex` attribute.

#### 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

### from Stimulus.js Controller

Stimulus.js controllers registered with StimulusReflex can use the `stimulate` method to trigger reflexes

#### 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

## Morphs

### Selector morphs

Instead of refreshing the entire page, you can specify a portion of the page to update with `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

Use `morph :nothing` in reflexes that do something on the server without updating the client.

    # 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

- `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

### Promises

`stimulate()` method returns a promise

    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.

    <!-- 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 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

    <!-- 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

### 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.

    <!-- 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>

### Aborting a reflex

call `raise :abort` within a reflex method to cancel it.

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

0 Comments for this cheatsheet. Write yours!