mcpField
F
form-auto-save

form-auto-save

github.com/rolemodel開発737 回インストール1 日前に更新
リポジトリ

form-auto-save について

Form Auto Save Skill は、変更後に Rails フォームを自動送信する Stimulus パターンを説明します。デバウンスでリクエストを抑え、長文や複数フィールドの編集画面を作る開発者向けです。

主な機能

  1. 01デバウンス付きフォーム自動送信
  2. 02既定の待ち時間は 8 秒で変更可能
  3. 03change と lexxy:change の監視
  4. 04プログラムからのキャンセルと送信
  5. 05保存カウントを使ったシステムテスト

活用シーン

  1. 01長文編集ページへの自動保存の導入
  2. 02リッチテキストフィールドの変更保存
  3. 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 を待機します。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 イベントを発生させていること(テキスト入力では、イベントの発生にフォーカスを外す必要がある場合があります)
  • ブラウザーの DevTools でネットワークリクエストを確認します
問題:リクエストが多すぎる

解決策:

  • DEBOUNCE_TIME を長くします
  • 不要なイベントが発生していないか確認します
  • デバウンスロジックが機能していることを確認します
問題:ページ移動時に変更が失われる

解決策:

  • フォームに turbo_permanent: true を追加します
  • フォームに安定した id 属性があることを確認します
  • 「未保存の変更」に関する警告の追加を検討します

関連パターン

  • 動的フォーム: フォームの入力に応じて変化するフィールドには、dynamic-forms スキル(turbo_form gem)を使用します
  • Stimulus Values: インスタンスごとにデバウンス時間を設定する必要がある場合
  • フォームのバリデーション: 自動保存とインラインバリデーションの併用を検討してください

参考資料

コメント