【Rails7】複数のチェックボックスの内容をDBに保存する
はじめに
こんにちは。Ruby on Railsを学習しているMockeryと申します。
今回は、非同期でチェックボックスの状態をDBに保存する実装方法です。
作成中のアプリにはTO DOリストがあり、複数のチェックボックスにチェックを入れた状態を保存したいと考えました。しかし、非同期でチェックしても、画面遷移やリロード後にチェックが外れてしまう問題が発生していました。
この記事では、チェックボックスの状態がDBに保存され、画面遷移後もチェックが維持されるようにする実装について記載します。
なお、非同期通信の具体的な実装(TurboやAjaxの設定)についてはこの記事では扱っていませんので、ご了承ください。
イメージ動画🔽
環境
- Ruby 3.2.3
- Ruby on Rails 7.1.3.4
DBの構造(該当箇所のみ)
TO DOのテーブルは以下のようにしています。
※boolean型 : データ型の一種で、「真(true)」か「偽(false)」が入る。
デフォルトはfalse = チェックが入っていない状態
create_table "todos", force: :cascade do |t|
t.bigint "trip_id", null: false
t.string "content", null: false
t.boolean "done", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["trip_id"], name: "index_todos_on_trip_id"
end
調査
参考記事によると、選択されたらtrue
, 選択解除はfalse
で選択状態を取得でき、JSのイベントリスナーを通して都度取得する必要があることが分かりました。
実施内容
チェックボックスに何も選択していない状態がfalse
で、選択した状態がtrue
になるように実装しました。流れは以下の通りです。
【動作の流れ】
- フォームが送信されると、隠しフィールド
todo[done]=0
が送信される。 - チェックボックスが選択されている場合、その値
todo[done]=1
が送信され、同じフィールド名で送信されるため、値が0
で上書きされる。 - チェックボックスがオフの場合、隠しフィールドの値
0
がそのまま送信される
1. 隠しフィールド(hidden_field)の追加
隠しフィールドを使用する理由は、チェックボックスがオフの状態でも値をサーバーに送信するためです。
デフォルトの場合、チェックボックスが選択されていないと、フォームを送信してもそのチェックボックスの値はフォームデータに含まれません。つまり、データベースに値が保存されない
ということです。
フォームが送信されると隠しフィールドも一緒に送信され、チェックされていない場合の 0 が確実に送信されるため、データベースが更新されます。
よって、データベースに正しい値(true/false)が保持されるようになります。
<li id="todo-<%= todo.id %>" class="flex justify-start items-center space-x-2">
<%= form_with(model: [ @trip, todo ], url: update_todo_trip_path(@trip, todo), method: :patch, data: { turbo_frame: "todo_#{todo.id}" }) do |f| %>
<%= f.hidden_field :done, value: 0 %> <!-- チェックボックスの初期値 -->
<%= f.check_box :done, checked: todo.done, { class: "form-checkbox h-6 w-6 text-purple-600" }, 1,0 %> <!-- チェックボックスを設定 -->
<%= f.label :content, todo.content %>
<% end %>
</li>
-
hidden_field: チェックボックスが選択されていない時に
done
フィールドの値として0
を送信するための隠しフィールドです。 -
check_box: チェックボックスが選択されているときに
1
, 選択されていない時に0
が送信されます。
2. JSの設定(イベントリスナー)
チェックボックスの状態下変更された時に自動的にフォームを送信するためにjavaScriptを設定します。
console.log('Script loaded'); // スクリプトが読み込まれたことを確認
document.addEventListener('turbo:load', () => {
console.log('turbo:load event fired'); // デバッグ用ログ
document.querySelectorAll('input[type="checkbox"][name="todo[done]"]').forEach((checkbox) => {
console.log('Adding event listener to checkbox'); // デバッグ用ログ
checkbox.addEventListener('change', (event) => {
console.log(`Checkbox changed: ${event.target.checked}`); // コンソールログを追加
checkbox.closest('form').requestSubmit();
});
});
});
【スクリプトについて】
document.addEventListener('turbo:load', () => {
console.log('turbo:load event fired');
🔼 turbo:load
というイベントが発生した時に実行される関数を設定。イベントはページが読み込まれた時に発生する。
document.querySelectorAll('input[type="checkbox"][name="todo[done]"]').forEach((checkbox) => {
console.log('Adding event listener to checkbox');
🔼 querySelectorAll
を使って、input
タグでタイプがチェックボックスで、名前がtodo[done]
の要素を全部探す。
🔼 forEach
は見つけたチェックボックスそれぞれに対して処理を行う。この場合、チェックボックスにイベントリスナーを追加する。
checkbox.addEventListener('change', (event) => {
console.log(`Checkbox changed: ${event.target.checked}`);
checkbox.closest('form').requestSubmit();
});
});
🔼checkbox.addEventListener('change', ...)
で、チェックボックスの状態が変わったときに実行される関数を設定。
🔼checkbox.closest('form').requestSubmit();
で、チェックボックスの状態が変わったときにその場でフォームが送信される。
【JSファイルの読み込み】
import "./todo"
を``application.js`に追加して読み込みます。
import "./todo"
動作確認
- JSが読み込まれて、イベントが発火しています。
【ログの確認】
- todo の done 属性が 1 つまり true に更新されていることが確認できます。
おわりに
複数のチェックボックスの値を保存するためには、チェックが外されている状態でも値をサーバーに送信する必要があります。そのため、隠しフィールドが重要な役割を担っています。
この実装を通して、チェックボックスのfalse の値を保存することで、データの一貫性を保つことができることを理解できました。
今回もご覧いただきありがとうございました。
Discussion