Skip to content

Latest commit

 

History

History
381 lines (269 loc) · 11.3 KB

README.md

File metadata and controls

381 lines (269 loc) · 11.3 KB

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 an element by default. XPath expressions can also be used if the xpath option is set to true. 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/after events triggered on document. These events expose event.detail set 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

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

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
  permanent_attribute_name: "string", # [null]   - an attribute name that prevents elements from being updated i.e. "data-permanent"
  focus_selector: "string",           # [null]   - string containing a CSS selector
)
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', '');
  }
}

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-html
  • cable-ready:after-inner-html

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-html
  • cable-ready:after-outer-html

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-content
  • cable-ready:after-text-content

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-html
  • cable-ready:after-insert-adjacent-html

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-text
  • cable-ready:after-insert-adjacent-text

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-remove
  • cable-ready:after-remove

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-value
  • cable-ready:after-set-value

Attribute Mutations

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-attribute
  • cable-ready:after-set-attribute

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-attribute
  • cable-ready:after-remove-attribute

CSS Class Mutations

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-class
  • cable-ready:after-add-css-class

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-class
  • cable-ready:after-remove-css-class

Dataset Mutations

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-property
  • cable-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

  1. Bump version number at lib/cable_ready/version.rb
  2. Run rake build
  3. Run rake release
  4. Change directories cd ./javascript
  5. 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.