Loading...
Loading...
Automatic form submission after user input changes using a debounce mechanism to prevent excessive server requests. Creates a seamless auto-save experience for forms with rich text editors or multiple fields.
npx skill4agent add rolemodel/rolemodel-skills form-auto-saveform-auto-saveapp/javascript/controllers/form_auto_save_controller.jsstatic DEBOUNCE_TIMEchangelexxy:changecancel()submit()import { Controller } from '@hotwired/stimulus'
export default class extends Controller {
static DEBOUNCE_TIME = 8000
connect() {
this.element.addEventListener('change', this.#debounceSubmit.bind(this), { passive: true })
this.element.addEventListener('lexxy:change', this.#debounceSubmit.bind(this), { passive: true })
}
cancel() {
clearTimeout(this.debounceTimer)
}
submit() {
this.element.requestSubmit()
}
#debounceSubmit() {
this.#debounce(this.submit.bind(this))
}
#debounce(callback) {
clearTimeout(this.debounceTimer)
this.debounceTimer = setTimeout(callback, this.constructor.DEBOUNCE_TIME)
}
}data: { controller: 'form-auto-save' }data: { turbo_permanent: true }= simple_form_for resource, html: { data: { controller: 'form-auto-save', turbo_permanent: true } } do |f|
= f.input :field_name
= f.rich_text_area :contentstatic DEBOUNCE_TIMEchangelexxy:changeturbo_permanent: trueturbo-fetchform-auto-saveapp/javascript/controllers/turbo_fetch_controller.jsimport { Controller } from '@hotwired/stimulus'
import { patch } from '@rails/request.js'
export default class extends Controller {
static values = {
url: String,
count: Number,
isRunning: { type: Boolean, default: false }
}
async perform({ params: { url: urlParam, query: queryParams } }) {
this.isRunningValue = true
const body = new FormData(this.element)
if (queryParams) Object.keys(queryParams).forEach(key => body.append(key, queryParams[key]))
const response = await patch(urlParam || this.urlValue, { body, responseKind: 'turbo-stream' })
this.isRunningValue = false
if (response.ok) this.countValue += 1
}
}spec/support/helpers/turbo_fetch_helper.rbmodule TurboFetchHelper
def expect_turbo_fetch_request
count_value = find("[data-controller='turbo-fetch']")['data-turbo-fetch-count-value'] || 0
yield
expect(page).to have_selector("[data-turbo-fetch-count-value='#{count_value.to_i + 1}']")
end
endturbo-fetchform-auto-save= simple_form_for resource, html: { data: { controller: 'form-auto-save turbo-fetch', turbo_permanent: true } } do |f|
= f.input :field_name
= f.rich_text_area :contentrequire 'rails_helper'
RSpec.describe 'Form Auto Save', :js do
it 'automatically saves form after changes' do
resource = create(:resource)
visit edit_resource_path(resource)
expect_turbo_fetch_request do
fill_in 'Field name', with: 'Updated value'
end
expect(resource.reload.field_name).to eq('Updated value')
end
it 'debounces multiple rapid changes' do
resource = create(:resource)
visit edit_resource_path(resource)
expect_turbo_fetch_request do
fill_in 'Field name', with: 'First'
fill_in 'Field name', with: 'Second'
fill_in 'Field name', with: 'Final'
end
# Should only save once with final value
expect(resource.reload.field_name).to eq('Final')
end
enddata: { controller: 'form-auto-save' }changeDEBOUNCE_TIMEturbo_permanent: trueid