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
- dispatchEvent
- morph
- innerHTML
- outerHTML
- insertAdjacentHTML
- insertAdjacentText
- remove
- setValue
- setAttribute
- removeAttribute
- addCssClass
- removeCssClass
- setDatasetProperty
The
selectoroptions 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/afterevents triggered ondocument. These events exposedetail.configset 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-morphcable-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-htmlcable-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-htmlcable-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-contentcable-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-htmlcable-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-textcable-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-removecable-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-valuecable-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-attributecable-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-attributecable-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-classcable-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-classcable-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-propertycable-ready:after-set-dataset-property
JavaScript Development
cd /path/to/cable_ready/javascript
vim ./cable_ready.js
yarn publish