Lines of Code Maintainability

CableReady

Out-of-Band Server Triggered DOM Operations

CableReady provides a simple interface for triggering client-side DOM operations from the server via ActionCable.

Please read the official ActionCable docs to learn more about ActionCable before proceeding.

Setup

JavaScript

yarn install cable_ready

Gemfile

gem "cable_ready"

Usage

app/assets/javascripts/channels/user.js

import CableReady from 'cable_ready';

App.cable.subscriptions.create({ channel: "UserChannel" }, {
  received: function (data) {
    if (data.cableReady) {
      CableReady.perform(data.operations);
    }
  }
});

app/models/user.rb

class User < ApplicationRecord
  include CableReady::Broadcaster

  def broadcast_name_change
    cable_ready["UserChannel"].text_content selector: "#user-name", text: name
    cable_ready.broadcast
  end
end

Supported DOM Operations

The selector options use Document.querySelector() to find elements.

It's possible to invoke multiple DOM operations with a single ActionCable broadcast.

All DOM mutations have corresponding before/after events triggered on document. These events expose detail.config set to the arguments from the server.

DOM Events

dispatchEvent

Dispatches a DOM event in the browser.

cable_ready["MyChannel"].dispatch_event(
  name:     "string", # required - the name of the DOM event to dispatch (can be custom)
  detail:   "object", # [null]   - assigned to event.detail
  selector: "string"  # [window] - string containing one or more CSS selectors separated by commas
)

Element Mutations

morph

Fast lightweight DOM diffing/patching without a virtual DOM.

cable_ready["MyChannel"].morph(
  selector:       "string",  # required - string containing one or more CSS selectors separated by commas
  html:           "string"   # [null]   - the HTML to assign
  children_only:  true|false # [null]   - indicates if only child nodes should be morphed... skipping the parent element
  focus_selector: "string",  # [null]   - string containing one or more CSS selectors separated by commas
)
JavaScript Events
  • cable-ready:before-morph
  • cable-ready:after-morph
Stimulus Gotchas

For some reason Stimulus controllers don't reconnect after DOM mutations triggered by Morphdom. You can force your controllers to reconnect with the following code.

import { Controller } from "stimulus"

export default class extends Controller {
  connect() {
    this.name = this.element.dataset.controller;
    document.addEventListener('cable-ready:after-morph', this.reconnect.bind(this));
    );
  }

  reconnect() {
    setTimeout(() => this.element.setAttribute('data-controller', this.name), 1);
    this.element.setAttribute('data-controller', '');
  }
}

innerHTML

Sets the innerHTML of a DOM element.

cable_ready["MyChannel"].inner_html(
  selector:       "string", # required - string containing one or more CSS selectors separated by commas
  focus_selector: "string", # [null]   - string containing one or more CSS selectors separated by commas
  html:           "string"  # [null]   - the HTML to assign
)
JavaScript Events
  • cable-ready:before-inner-html
  • cable-ready:after-inner-html

outerHTML

Replaces a DOM element with new HTML.

cable_ready["MyChannel"].outerHTML(
  selector:       "string", # required - string containing one or more CSS selectors separated by commas
  focus_selector: "string", # [null]   - string containing one or more CSS selectors separated by commas
  html:           "string"  # [null]   - the HTML to use as replacement
)
JavaScript Events
  • cable-ready:before-outer-html
  • cable-ready:after-outer-html

textContent

Sets the text content of a DOM element.

cable_ready["MyChannel"].text_content(
  selector: "string", # required - string containing one or more CSS selectors separated by commas
  text:     "string"  # [null]   - the text to assign
)
JavaScript Events
  • cable-ready:before-text-content
  • cable-ready:after-text-content

insertAdjacentHTML

Inserts HTML into the DOM relative to an element. Supports behavior akin to prepend & append.

cable_ready["MyChannel"].insert_adjacent_html(
  selector:       "string", # required    - string containing one or more CSS selectors separated by commas
  focus_selector: "string", # [null]      - string containing one or more CSS selectors separated by commas
  position:       "string", # [beforeend] - the relative position to the DOM element (beforebegin, afterbegin, beforeend, afterend)
  html:           "string"  # [null]      - the HTML to insert
)
JavaScript Events
  • cable-ready:before-insert-adjacent-html
  • cable-ready:after-insert-adjacent-html

insertAdjacentText

Inserts text into the DOM relative to an element. Supports behavior akin to prepend & append.

cable_ready["MyChannel"].insert_adjacent_text(
  selector: "string", # required    - string containing one or more CSS selectors separated by commas
  position: "string", # [beforeend] - the relative position to the DOM element (beforebegin, afterbegin, beforeend, afterend)
  text:     "string"  # [null]      - the text to insert
)
JavaScript Events
  • cable-ready:before-insert-adjacent-text
  • cable-ready:after-insert-adjacent-text

remove

Removes an element from the DOM.

cable_ready["MyChannel"].remove(
  selector:       "string", # required - string containing one or more CSS selectors separated by commas
  focus_selector: "string"  # [null]   - string containing one or more CSS selectors separated by commas
)
JavaScript Events
  • cable-ready:before-remove
  • cable-ready:after-remove

setValue

Sets the value of an element.

cable_ready["MyChannel"].set_value(
  selector: "string", # required - string containing one or more CSS selectors separated by commas
  value:    "string"  # [null]   - the value to assign to the attribute
)
JavaScript Events
  • cable-ready:before-set-value
  • cable-ready:after-set-value

Attribute Mutations

setAttribute

Sets an attribute on an element.

cable_ready["MyChannel"].set_attribute(
  selector: "string", # required - string containing one or more CSS selectors separated by commas
  name:     "string", # required - the attribute to set
  value:    "string"  # [null]   - the value to assign to the attribute
)
JavaScript Events
  • cable-ready:before-set-attribute
  • cable-ready:after-set-attribute

removeAttribute

Removes an attribute from an element.

cable_ready["MyChannel"].remove_attribute(
  selector: "string", # required - string containing one or more CSS selectors separated by commas
  name:     "string"  # required - the attribute to remove
)
JavaScript Events
  • cable-ready:before-remove-attribute
  • cable-ready:after-remove-attribute

CSS Class Mutations

addCssClass

Adds a css class to an element. This is a noop if the css class is already assigned.

cable_ready["MyChannel"].add_css_class(
  selector: "string", # required - string containing one or more CSS selectors separated by commas
  name:     "string"  # [null]   - the CSS class to add
)

JavaScript Events
  • cable-ready:before-add-css-class
  • cable-ready:after-add-css-class

removeCssClass

Removes a css class from an element.

cable_ready["MyChannel"].add_css_class(
  selector: "string", # required - string containing one or more CSS selectors separated by commas
  name:     "string"  # [null]   - the CSS class to remove
)
JavaScript Events
  • cable-ready:before-remove-css-class
  • cable-ready:after-remove-css-class

Dataset Mutations

setDatasetProperty

Sets an dataset property (data-* attribute) on an element.

cable_ready["MyChannel"].set_dataset_property(
  selector: "string", # required - string containing one or more CSS selectors separated by commas
  name:     "string", # required - the property to set
  value:    "string"  # [null]   - the value to assign to the dataset
)
JavaScript Events
  • cable-ready:before-set-dataset-property
  • cable-ready:after-set-dataset-property

JavaScript Development

cd /path/to/cable_ready/javascript
vim ./cable_ready.js
yarn publish