关于 form-auto-save
Form Auto Save Skill 介绍一种 Stimulus 模式,可在 Rails 表单发生更改后自动提交,并通过防抖减少请求。它面向构建长文本或多字段编辑界面的开发者。
主要功能
- 01防抖式自动提交表单
- 02默认延迟 8 秒且可配置
- 03监听 change 和 lexxy:change
- 04提供程序化取消与提交方法
- 05通过保存计数测试系统规格
适用场景
- 01为长文本编辑页面添加自动保存
- 02保存富文本字段的更改
- 03避免固定休眠计时器来测试保存
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ rolemodel/ rolemodel-skills --skill form-auto-save安装后在 Claude Code 中输入/form-auto-save确认已加载。
详细介绍
表单自动保存技能
概述
表单自动保存模式会在用户输入发生变化后自动提交表单,并使用防抖机制避免向服务器发送过多请求。这能为编辑表单的用户带来流畅的“自动保存”体验。
适用场景
- 用户期望自动保存的长篇内容编辑界面
- 包含富文本编辑器或多个字段的表单
- 用户可能离开页面并期望更改得以保留的编辑页面
- 适合逐步保存、无需显式点击“保存”按钮的表单
实现
1. Stimulus 控制器
此模式使用 Stimulus 控制器(form-auto-save)处理自动保存逻辑。
控制器位置: app/javascript/controllers/form_auto_save_controller.js
主要功能:
- 防抖时间为 8 秒(可通过
static DEBOUNCE_TIME配置) - 监听
change和lexxy:change事件(用于自定义组件) - 使用被动事件监听器以提升性能
- 提供
cancel()和submit()方法,用于编程控制 - 在
countValue中统计成功保存的次数,系统规格测试会等待此值变化
控制器代码模式:
import { Controller } from '@hotwired/stimulus'
export default class extends Controller {
static DEBOUNCE_TIME = 8000
static values = { count: Number }
connect() {
this.element.addEventListener('change', this.#debounceSubmit.bind(this), { passive: true })
this.element.addEventListener('lexxy:change', this.#debounceSubmit.bind(this), { passive: true })
this.element.addEventListener('turbo:submit-end', ({ detail }) => { if (detail.success) this.countValue++ })
}
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)
}
}
2. 视图集成
使用 Stimulus 数据属性将控制器附加到表单元素。
必需属性:
data: { controller: 'form-auto-save' }- 附加 Stimulus 控制器data: { turbo_permanent: true }- 可选,但建议启用,以便在 Turbo 导航期间保留表单状态
示例(Slim):
= simple_form_for resource, html: { data: { controller: 'form-auto-save', turbo_permanent: true } } do |f|
= f.input :field_name
= f.rich_text_area :content
重要注意事项
防抖时间
- 默认值:8 秒(8000ms)
- 如有需要,可在控制器中通过
static DEBOUNCE_TIME调整 - 请考虑用户体验:时间过短 = 请求过多;时间过长 = 更改可能丢失
事件监听器
- 监听
change事件(标准 HTML 输入项的更改) - 监听
lexxy:change事件(自定义组件事件,例如富文本编辑器) - 使用被动监听器以提升滚动性能
Turbo Permanent
turbo_permanent: true会在 Turbo 导航期间保留表单元素- 可避免用户导航时丢失未保存的更改
- 对于带有自动保存功能的表单,此设置对保留防抖计时器至关重要
表单验证
- 确保后端验证能够妥善处理部分保存
- 考虑是否所有字段都必须填写,还是允许部分完成
- 如果自动保存失败,请提供清晰的错误反馈
测试
应等待控制器的 countValue,而不是使用 sleep 计时器。当 Turbo 报告提交成功时,该值会递增,因此它涵盖了防抖等待和请求往返时间。turbo_permanent: true 会在响应重定向后保留表单及其计数值。
自动保存辅助模块
文件: spec/support/helpers/form_auto_save_helper.rb
module FormAutoSaveHelper
def expect_auto_save
form = find("[data-controller~='form-auto-save']")
count = form['data-form-auto-save-count-value'].to_i
yield
expect(page).to have_css("[data-form-auto-save-count-value='#{count + 1}']", wait: 10)
end
end
在 spec/support/helpers.rb 中使用 c.include FormAutoSaveHelper, type: :system 将其引入系统规格测试。wait: 必须长于 DEBOUNCE_TIME。
系统规格测试示例
require '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_auto_save 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_auto_save 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
end
常见问题
问题:表单不会自动保存
检查:
- 控制器是否已正确附加:
data: { controller: 'form-auto-save' } - 表单字段是否会触发
change事件(文本输入框可能需要失焦) - 浏览器开发者工具中的网络请求
问题:请求过多
解决方案:
- 增大
DEBOUNCE_TIME - 检查是否存在不必要的事件触发
- 确认防抖逻辑正常工作
问题:导航时更改丢失
解决方案:
- 为表单添加
turbo_permanent: true - 确保表单具有稳定的
id属性 - 考虑添加“存在未保存更改”警告
相关模式
- 动态表单: 随表单填写而变化的字段属于
dynamic-forms技能(turbo_form gem) - Stimulus 值: 如果需要为不同实例设置防抖时间
- 表单验证: 可考虑结合自动保存使用内联验证
参考资料
- Stimulus 控制器 API:https://stimulus.hotwired.dev/
- Turbo Permanent:https://turbo.hotwired.dev/handbook/building#persisting-elements-across-page-loads
评论
支持 Markdown