Last active
February 17, 2022 14:54
-
-
Save rainerborene/644da1be59ecb4f6c4c9637abf2622e0 to your computer and use it in GitHub Desktop.
rails-ujs is a past thing. Here is how you can accomplish almost the same behaviour with Stimulus and Hotwire.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export function reduceToParams(elements) { | |
return elements.reduce((hash, node) => Object.assign(hash, { [node.name]: node.value }), {}) | |
} | |
export function stopEverything(e) { | |
e.preventDefault() | |
e.stopPropagation() | |
e.stopImmediatePropagation() | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ApplicationController from "controllers/application_controller" | |
import { useMeta } from "stimulus-use" | |
import { stopEverything } from "helpers/dom" | |
import request from "helpers/request" | |
import RemoteForm from "helpers/remote_form" | |
import { reduceToParams } from "helpers/dom" | |
import { toggleSubmitDisabled } from "helpers/turbo" | |
export default class extends ApplicationController { | |
static values = { url: String, method: String, target: String } | |
static metaNames = ["csrf-param", "csrf-token"] | |
static targets = ["input"] | |
connect() { | |
useMeta(this) | |
} | |
// Actions | |
async submit(event) { | |
if (event) stopEverything(event) | |
const form = new RemoteForm({ | |
action: this.urlValue, | |
method: this.methodValue, | |
target: this.targetValue, | |
params: reduceToParams(this.inputTargets) | |
}) | |
form.connect() | |
toggleSubmitDisabled(this.element, true) | |
await form.submit() | |
toggleSubmitDisabled(this.element, false) | |
this.dispatch("success") | |
form.disconnect() | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export default class RemoteForm { | |
constructor(options) { | |
this.options = options | |
this.options.params = this.options.params ?? {} | |
this.element = document.createElement("form") | |
this.element.action = this.action | |
this.element.method = this.method | |
this.element.target = this.target | |
this.element.style.display = "none" | |
} | |
// Actions | |
connect() { | |
document.body.appendChild(this.element) | |
} | |
disconnect() { | |
this.element.remove() | |
} | |
submit() { | |
const scrollTop = document.scrollingElement.scrollTop | |
this.reset() | |
for (const [key ,value] of Object.entries(this.params)) { | |
this.append(key, value) | |
} | |
return new Promise(resolve => { | |
this.element.requestSubmit() | |
if (this.turboDisabled) { | |
return resolve() | |
} | |
this.element.addEventListener("turbo:submit-end", () => { | |
document.scrollingElement.scrollTo(0, scrollTop) | |
resolve() | |
}, { once: true }) | |
}) | |
} | |
set(name, value) { | |
this.options.params[name] = value | |
return this | |
} | |
// Private | |
get turboDisabled() { | |
return this.element.closest("[data-turbo=false]") !== null | |
} | |
get action() { | |
return this.options.action | |
} | |
get method() { | |
return this.options.method === "get" ? "get" : "post" | |
} | |
get target() { | |
return this.options.target | |
} | |
get csrfParamName() { | |
return getMeta("csrf-param") | |
} | |
get csrfToken() { | |
return getMeta("csrf-token") | |
} | |
get railsParams() { | |
if (this.options.method === "get") { | |
return {} | |
} else { | |
return { _method: this.options.method, [this.csrfParamName]: this.csrfToken } | |
} | |
} | |
get params() { | |
return Object.assign({}, this.railsParams, this.options.params) | |
} | |
get inputs() { | |
return [...this.element.querySelectorAll("input[type=hidden]")] | |
} | |
reset() { | |
this.inputs.forEach(input => input.remove()) | |
} | |
append(name, value) { | |
const input = document.createElement("input") | |
input.type = "hidden" | |
input.name = name | |
input.value = value | |
this.element.appendChild(input) | |
} | |
} | |
function getMeta(name) { | |
const meta = document.querySelector(`meta[name=${name}]`) | |
return meta && meta.content | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { navigator } from "@hotwired/turbo" | |
const { adapter } = navigator.delegate | |
export function showProgressBar() { | |
adapter.progressBar.setValue(0) | |
adapter.progressBar.show() | |
} | |
export function hideProgressBar() { | |
adapter.progressBar.setValue(1) | |
adapter.progressBar.hide() | |
} | |
export function setDisableWithText(element) { | |
if (element instanceof HTMLButtonElement) { | |
element.olderInnerHTML = element.innerHTML | |
element.innerHTML = element.dataset.disableWith | |
} else if (element instanceof HTMLInputElement) { | |
element.olderValue = element.value | |
element.value = element.dataset.disableWith | |
} | |
} | |
export function undoDisableWithText(element) { | |
if (element instanceof HTMLButtonElement) { | |
element.innerHTML = element.olderInnerHTML | |
} else if (element instanceof HTMLInputElement) { | |
element.value = element.olderValue | |
} | |
} | |
export function enableSubmits(submits) { | |
submits.forEach(element => { | |
if (element.dataset.disableWith) { | |
undoDisableWithText(element) | |
} | |
element.disabled = false | |
}) | |
} | |
export function disableSubmits(submits) { | |
submits.forEach(element => { | |
if (element.dataset.disableWith) { | |
setDisableWithText(element) | |
} | |
element.disabled = true | |
}) | |
} | |
export function toggleSubmitDisabled(form, toggle) { | |
const submits = [...form.querySelectorAll("button[type=submit], input[type=submit]")] | |
if (toggle) { | |
disableSubmits(submits) | |
} else { | |
enableSubmits(submits) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment