【Rails6】ラジオボタンの使いづらさ解消 ＆ Bootstrap5.3を使ったレイアウト調整

# はじめに
Railsを使って学習を進めていると、機能として動いてはいるんだけどなんだか使いづらい…という場面に多々遭遇します。
今回は、絶妙に使いづらかったラジオボタンの使い勝手を、ちょっとだけ向上させたときの内容をご紹介します。

## 基本の記述
form_withでtext_fieldなどを呼び出すときのように、ラジオボタンを作成しようと思うと、以下のような形になります。
```html:絶妙に使いづらいラジオボタン
<%= form_with model: @item, url: items_path do |form| %>
    
    <%= form.radio_button :is_active, :true %>
    <%= form.label :is_active, "公開" %>
    
    <%= form.radio_button :is_active, :false %>
    <%= form.label :is_active, "非公開" %>

<% end %>
```
これでもラジオボタン自体は表示されるのですが、このままではラジオボタン部分からしか選択できず、labelに指定した「公開」と「非公開」の文字を押してもフォーム部分が反応してくれません。

この問題に関しては単純で、こちらの記事を参考にさせていただきました。

https://qiita.com/RouxRhett/items/7c2bd566c4f3ee0d6281

つまり、先ほどのラジオボタンの場合、label部分の指定を変えることでクリック判定を付けることが出来るようです。

```diff_html:ちょっと使いやすいラジオボタン
<%= form_with model: @item, url: items_path do |form| %>

     <%= form.radio_button :is_active, :true %>
-    <%= form.label :is_active, "公開" %>
+    <%= form.label :is_active_true, "公開" %>
    
     <%= form.radio_button :is_active, :false %>
-    <%= form.label :is_active, "非公開" %>
+    <%= form.label :is_active_false, "非公開" %>

<% end %>
```

単純ですが、これだけでラジオボタンの使いやすさ全然違います。

## Bootstrapを使ったレイアウト調整

まず前提として、私は、bootstrap5.3をCDNで導入しています。
他のバージョンですと上手く反映されない場合もありますのでご注意ください。

ちなみに完成イメージはこんな感じです。

![2024-05-31ラジオボタン.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3762011/f8649b41-bc10-6256-9449-aaccd6b83e83.gif)

### このラジオボタンを作るためのビューがこちら…
```html:Bootstrapを適応したラジオボタン
<%= form_with model: @item, url: items_path do |form| %>
    <%= form.radio_button :is_active, true, class: "form-check-input" %>
    <%= form.label :is_active_true, "公開", class: "form-check-label text-success fw-bold ps-2" %>
    
    <%= form.radio_button :is_active, false, class: "form-check-input" %>
    <%= form.label :is_active_false, "非公開", class: "form-check-label text-danger fw-bold ps-2" %>
<% end %>
```
細かい文字色やすき間の設定については置いておいて、フォーム自体は単純にBootstrapが用意してくれている<b>「form-check-label」</b>や<b>「form-check-input」を設定してます。

<b>「label」</b>に<b>「form-check-label」</b>を指定するのはなんとなく分かると思いますが、
<b>「radio_button」</b>に<b>「form-check-input」</b>を設定する理由はRailsガイドを見るとはっきりと分かります。

https://railsguides.jp/v6.1/form_helpers.html#%E3%83%A9%E3%82%B8%E3%82%AA%E3%83%9C%E3%82%BF%E3%83%B3

><%= form.radio_button :age, "child" %>
<%= form.label :age_child, "I am younger than 21" %>
<%= form.radio_button :age, "adult" %>
<%= form.label :age_adult, "I am over 21" %>
>
>出力は以下のようになります。
>
><&#8203;input type="radio" id="age_child" name="age" value="child" />
<&#8203;label for="age_child">I am younger than 21</label>
<&#8203;input type="radio" id="age_adult" name="age" value="adult" />
<&#8203;label for="age_adult">I am over 21</label>

このように、<b>「form.radio_button」</b>を記述するとHTMLとしてはinputタグが生成されます。

合わせてBootstrapのチェックボックスを作るための記述例を見ると、

https://getbootstrap.jp/docs/5.3/forms/checks-radios/#%e3%83%81%e3%82%a7%e3%83%83%e3%82%af%e3%83%9c%e3%83%83%e3%82%af%e3%82%b9

><&#8203;input class="form-check-input" type="checkbox" value="" id="flexCheckDefault">
<&#8203;label class="form-check-label" for="flexCheckDefault">
    Default checkbox
<&#8203;/label>

inputタグにform-check-inputが指定されているのが分かります。
これでformタグへのclass指定の意味がつながりました。

:::note info
ちなみに、Railsガイドのラジオボタンの説明をよく見ると、
<b><%= form.radio_button :age, "child" %>
<%= form.label :age_child, "I am younger than 21" %></b>
のように、ラベル部分もクリックできるようになる記述方法で書かれていました。
最初のクリック判定もきちんと公式に明記されていたようです。
しっかりと公式から正しい記述を確認することの重要性が分かりますね…。
:::


### Bootstrapの色を変更

絶妙にラジオボタンの色がサイトの雰囲気に合わなかったので、変更することにしました。

```css:scss
label.text-danger {
    color: #C33E4C!important;
}

label.text-success {
    color: #008276!important;
}

.form-check-input:checked {
  border-color: #EAEAEA!important;
  background-color: #F69BA2!important;
}
```
ちなみに、既に記述してしまっていた他の部分もまとめて変更したかったため、今回はBootstrapの「text-danger」や「text-success」を直接上書きしています。

チェックした時の色は「.form-check-input:checked」で設定しています。



## 検索後にラジオボタンのチェックが外れないようにする
検索フォームにラジオボタンを設定していると、検索後の画面でラジオボタンが外れてしまい、連続して検索したいときが少し使いづらくなってしまいます。

そこで、検索後に、ラジオボタンが選択されている状態かどうかを判定してビューへ渡すことにします。

完成イメージはこんな感じです。

![2024-05-31検索.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3762011/d8e41eef-3ed8-d1e1-5524-d5c495db90f9.gif)


### コントローラの記述

※検索のロジックについてはややこしくなっているので、今回はある程度省略しています。
```ruby:searches_controller.rb
  def search
:
    @content = params[:content]
    @model = params[:model].presence || 'item'
    @search = if @model.in?(['address', 'shop'])
                { model: @model }
              else
                { model: 'item' }
              end

    if @search[:model] == 'item'
      @records = Item.where('name LIKE? OR introduction LIKE?','%'+@content+'%','%'+@content+'%')
                     .order(updated_at: 'DESC')
:
:
    end
:
:
end
```

ポイントは、<b>@&#8203;search</b>の部分です。
今回は検索方法をmodelで指定しており、デフォルトを<b>「商品（item）」</b>検索にしたかったのでmodelが送信されていない状態ではitemにチェックが入るようにしています。

<b>@&#8203;modelが「住所（address）」か 「ショップ（shop）」</b>だったらそのまま<b>@&#8203;model</b>を指定して、そうでなければ<b>「商品（item）」</b>を指定してます。

### ビューの記述

先ほどコントローラで定義した「@&#8203;search」をビューで呼び出して、ラジオボタンが選択されているかどうかの判定を行います。

```html:text
<%= f.radio_button :model, 'item', checked: @search[:model] == 'item', class: "form-check-input" %><%= f.label :model_item, "商品", class: "form-check-label text-white fw-bold ps-2" %>
<%= f.radio_button :model, 'address', checked: @search[:model] == 'address', class: "form-check-input" %><%= f.label :model_address, "住所", class: "form-check-label text-white fw-bold ps-2" %>
<%= f.radio_button :model, 'shop', checked: @search[:model] == 'shop', class: "form-check-input" %><%= f.label :model_shop, "ショップ", class: "form-check-label text-white fw-bold ps-2" %>
```
<b>「checked: @&#8203;search[:model] == 'item'」</b>の部分で判定しています。

checked属性についてはこちら…

https://developer.mozilla.org/ja/docs/Web/HTML/Element/input/checkbox#checked

>論理属性で、チェックボックスが既定で（ページが読み込まれたときに）チェックされているかどうかを示します。チェックボックスが現在チェックされているかどうかを示すものではありません。チェックボックスの状態が変化した場合、このコンテンツ属性は変化を反映しません。（HTMLInputElement の checked IDL 属性のみが更新されます。）

今回の場合、<b>@&#8203;search[:model] == 'item'がtrue</b>の場合、checkedが設定され、このラジオボタンが選択された状態になります。

これで、私が気になったラジオボタンの使い勝手の悪さ解消はひとまず完了です。

# おわりに

いかかでしょう？
ちょっとしたことですが、これだけで操作時のストレスが少し軽減されたのではないでしょうか。

機能として取り入れるだけだと単純ですが、使いやすさを考えるといくらでも改良の余地はあることが分かります。
まだまだ修正できる部分はあるはずなので、ユーザービリティを考慮しながら開発を進められるように気を配っていきたいです…！