【Rails】checkboxを用いて曜日を複数選択させる入力フォームを作る


はじめに

checkboxで1つ以上選択必須の入力フォームを作っていきます。
今回は、チームを新規作成する際に活動曜日を最低1つ以上、かつ複数選択可の入力フォームを作っていきます。

曜日カラムを作成する

Teamモデルは既に作成してある前提で進めていきます。
私は曜日ごとのカラムをboolean型で作成しました。

$ rails g migration AddWeeklyToTeams

下記のように記述します。

生成したマイグレーションファイル
class AddWeeklyToTeams < ActiveRecord::Migration[6.1]
  def change
    add_column :teams, :activity_monday, :boolean
    add_column :teams, :activity_tuesday, :boolean
    add_column :teams, :activity_wednesday, :boolean
    add_column :teams, :activity_thursday, :boolean
    add_column :teams, :activity_friday, :boolean
    add_column :teams, :activity_saturday, :boolean
    add_column :teams, :activity_sunday, :boolean
  end
end

マイグレーションを行います。

$ rails db:migrate

Viewファイルを記述する

まず活動曜日のカラムを配列で取り出せるようにweekly_columnsを定義します。

app/helpers/teams_helper.rb
def weekly_columns
        ['activity_monday', 'activity_tuesday', 'activity_wednesday',\
        'activity_thursday', 'activity_friday', 'activity_saturday', 'activity_sunday']
end

次にViewファイルを書いていきます。

app/views/teams/new.html.erb
<%= form_with(model:@team,local:true) do |f| %>
    <p>活動曜日</p>
    <span class = "checkbox-inline">
        <%  weekly_columns.each_with_index do |day,i| %>
            <%= f.check_box day.to_sym , {} ,"true", "false" %>
            <%= f.label day.to_sym , ["月","火","水","木","金","土","日"][i] %>
        <% end %>
    </span>
<% end %>

ここでは、each_with_indexを使って取り出した要素に番号を付けています。
f.label では取り出したカラム名に対応した曜日を表示し、
to_symでは取り出したカラム名をシンボルにしています。

スタイルを少し整えて次のようになります。

しかしこのままでは、曜日を一つも選択していない場合でも登録ができてしまいます。

独自バリデーションを作成する

独自バリデーションを作って、全てのcheckboxfalseの場合にはエラーを返します。

app/models/teams.rb
class Team < ApplicationRecord
    # 曜日カラムをtrueとfalseしか受け取らないようにする
    with_options inclusion: {in: [true, false]} do
        validates :activity_monday
        validates :activity_tuesday
        validates :activity_wednesday
        validates :activity_thursday
        validates :activity_friday
        validates :activity_saturday
        validates :activity_sunday
    end
    #ここからが独自バリデーション
    validate :weekly_checked

    #曜日が1つ以上選択されていること
    def weekly_checked
      if activity_monday == false && activity_tuesday == false && activity_wednesday == false \
        && activity_thursday == false && activity_friday == false && activity_saturday == false && activity_sunday == false
        errors.add(:weekly,"を1つ以上選択してください")  
      end
    end  
end

バリデーションに引っかかった際にはcheckboxの下に
エラーメッセージを表示するようにします。

app/views/teams/new.html.erb
<%= form_with(model:@team,local:true) do |f| %>
    <p>活動曜日</p>
    <span class = "checkbox-inline">
        <%  weekly_columns.each_with_index do |day,i| %>
            <%= f.check_box day.to_sym , {} ,"true", "false" %>
            <%= f.label day.to_sym , ["月","火","水","木","金","土","日"][i] %>
        <% end %>
    </span>
    <%# 追記 %> 
   <%= render 'shared/form_error_messages', object: f.object, attribute: :weekly %>
<% end %>
app/views/shared/_form_error_messages.html.erb
<% if object.errors.any? %>
    <div class="form-alert-danger">
        <ul>
            <% object.errors.full_messages_for(attribute).each do |msg| %>
             <li><%= msg %></li>
            <% end %>
        </ul>
    </div>
<% end %>

エラーメッセージを日本語化します。

app/config/locales/devise.ja.yml
ja:
  activerecord:
    attributes:
     team:
      weekly: 活動曜日

checkboxを一つも選択せずに登録をしようとすると、次のようなエラー文が表示されます。

チームプロフィールで活動曜日を表示する

各曜日カラムの中でtrueの曜日だけ表示していきます。

app/views/teams/show.html.erb
<p>活動曜日</p>
<%  weekly_columns.each_with_index do |day,i| %>
    <% if @team[day] == true %>
     <a><%= ["月","火","水","木","金","土","日"][i] %></a>
    <% end %>
<% end %>

最後に

曜日を1つ以上選択必須かつ、複数選択できる入力フォームを実装するために
色々と調べましたがドンピシャな記事を見つけることができませんでした...。
しかし、DB設計からかなり苦戦しながらも、なんとか実装することができました。
同じ悩みを抱えている方の参考になれば幸いです!