Skip to content

Instantly share code, notes, and snippets.

@firewalker06
Last active January 24, 2025 01:05
Show Gist options
  • Save firewalker06/bcef39c6ec7002ba06e5085dd72f9dc2 to your computer and use it in GitHub Desktop.
Save firewalker06/bcef39c6ec7002ba06e5085dd72f9dc2 to your computer and use it in GitHub Desktop.
Simple Autosave Controller in Stimulus. Inspired by Writebook codebase https://once.com/writebook
import { Controller } from "@hotwired/stimulus"
import { submitForm } from "helpers/form_helpers"
const AUTOSAVE_INTERVAL = 3000
export default class extends Controller {
static classes = [ "clean", "dirty", "saving" ]
#timer
// Lifecycle
disconnect() {
this.submit()
}
// Actions
async submit() {
if (this.#dirty) {
await this.#save()
}
}
change(event) {
if (event.target.form === this.element && !this.#dirty) {
this.#scheduleSave()
this.#updateAppearance()
}
}
// Private
async #save() {
this.#updateAppearance(true)
this.#resetTimer()
await submitForm(this.element)
this.#updateAppearance()
}
#updateAppearance(saving = false) {
this.element.classList.toggle(this.cleanClass, !this.#dirty)
this.element.classList.toggle(this.dirtyClass, this.#dirty)
this.element.classList.toggle(this.savingClass, saving)
}
#scheduleSave() {
this.#timer = setTimeout(() => this.#save(), AUTOSAVE_INTERVAL)
}
#resetTimer() {
clearTimeout(this.#timer)
this.#timer = null
}
get #dirty() {
return !!this.#timer
}
}
<%= form_with model: Model,
data: {
controller: "autosave",
action: "autosave#submit:prevent input@document->autosave#change formelement:change->autosave#change",
autosave_clean_class: "clean",
autosave_dirty_class: "dirty",
autosave_saving_class: "saving"
} %>
import { FetchRequest } from "@rails/request.js"
export async function submitForm(form) {
const request = new FetchRequest(form.method, form.action, {
body: new FormData(form)
})
return await request.perform()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment