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 an element by default. XPath expressions can also be used if thexpathoption is set totrue. As with CSS selectors, the XPath expression must resolve to a single element and not a collection.It's possible to invoke multiple DOM operations with a single ActionCable broadcast.
All DOM mutations have corresponding
before/afterevents triggered ondocument. These events exposeevent.detailset to the arguments from the server.
Xpath Example
app/models/user.rb
class User < ApplicationRecord
include CableReady::Broadcaster
def broadcast_name_change
cable_ready["UserChannel"].text_content selector: "/html/body/div[1]/form/input[1]", text: name, xpath: true
cable_ready.broadcast
end
end
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 a CSS selector or XPath expression
)
Element Mutations
morph
Fast lightweight DOM diffing/patching without a virtual DOM.
cable_ready["MyChannel"].morph(
selector: "string", # required - string containing a CSS selector or XPath expression
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 a CSS selector
)
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 a CSS selector or XPath expression
focus_selector: "string", # [null] - string containing a CSS selector
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 a CSS selector or XPath expression
focus_selector: "string", # [null] - string containing a CSS selector
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 a CSS selector or XPath expression
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 a CSS selector or XPath expression
focus_selector: "string", # [null] - string containing a CSS selector
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 a CSS selector or XPath expression
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 a CSS selector or XPath expression
focus_selector: "string" # [null] - string containing a CSS selector
)
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 a CSS selector or XPath expression
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 a CSS selector or XPath expression
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 a CSS selector or XPath expression
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 a CSS selector or XPath expression
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 a CSS selector or XPath expression
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 a CSS selector or XPath expression
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
Please run bin/standardize on your codebase before submitting commits.
Contributing
This project uses Standard
and Prettier to minimize bike shedding related to code formatting.
Please run ./bin/standardize prior submitting pull requests.
Releasing
- Bump version number at
lib/cable_ready/version.rb - Run
rake build - Run
rake release - Change directories
cd ./javascript - Run
yarn publish- NOTE: this will throw a fatal error because the tag already exists but the package will still publish
License
CableReady is released under the MIT License.