【ラジオボタンをeachで設置】戻るボタンを押した時に選択したラジオボタンがセットされるようにする

はじめに

タイトルがわかりにくいですね。。。

説明するとざっくりこんな感じです。

1.入力ページに選択肢(ラジオボタン)があるとします。

2.入力が完了して入力内容を確認するページに飛んで、この時に入力内容を修正したいなと思います。

3.戻るボタンを押して、入力ページに戻ります。

4.入力ページに戻った時に選択したラジオボタンがセットされるようにします。(例えばラジオボタンが三つあったとして、一つ目を選択したら戻った際に一つ目にセットされる、二つ目を選択されてたら戻った際に二つ目がセットされる)

動きとしてはこんな感じです。

Image from Gyazo

コードはこのようになります

= form_for @test, url: confirm_test_path do |f|
  = f.label :name
  %br/
  = f.text_field :name, placeholder: "名前を入力してください"
  - Course.where(id: 1..3).each_with_index do |course, i|
    - if params[:test]
     # 戻るボタンを押した際に選択したラジオボタンに値をセットする
      = f.radio_button :course_id, "#{course.id}", checked: "#{course.id == params[:test][:course_id].to_i ? 'checked' : '' }", id: "course-#{course.id}"
      %label.radio{for: "course-#{course.id}"}= course.fee_i18n
    - else
     # 初めて入力画面に移動した時に適用される
      = f.radio_button :course_id, "#{course.id}", checked: "#{i == 0 ? 'checked' : '' }", id: "course-#{course.id}"
      %label.radio{for: "course-#{course.id}"}= course.fee_i18n
  = f.submit "確認画面に進む"

実装過程

条件

ラジオボタンは配列に格納されたデータを取得してeach文で一つずつ表示されるようにしています。

最初はこのようにコードを書きました。

入力ページに飛んだ時は左のラジオボタンにセットされるようにしています。

ビュー

= form_for @test, url: confirm_test_path do |f|
  = f.label :name
  %br/
  = f.text_field :test, placeholder: "名前を入力してください"
  - Course.where(id: 1..3).each_with_index do |course, i|
     = f.radio_button :course_id, "#{course.id}", checked: "#{i == 0 ? 'checked' : '' }", id: "course-#{course.id}"
     %label.radio{for: "course-#{course.id}"}= course.fee_i18n
  = f.submit "確認画面に進む"

コントローラ

class TestController < ApplicationController
  # 入力ページ
  def new
    @test = Test.new
  end

  # 確認ページ
  def confirm
    @test = Test.new(test_params)
    @params = test_params
    @course = Course.find_by(id: @params[:course_id])
    render action: :new if @test.invalid?
  end

  def create
    @test = Test.new(test_params)
    if params[:back]
      render :new
    else
      @test.save
      redirect_to root_path
    end
  end

  private

  def test_params
    params.require(:test).permit(:name, :course_id)
  end
end

これで、入力確認ページから戻るとこうなります。

Image from Gyazo

試行錯誤

戻るボタンを押したら選択していないラジオボタンにセットされていますね。。。

パラメーターを受け取れていないのかな

デバッグして確かめてみます。

 pry(#<#<Class:0x00007f8f77e3efc8>>)> params[:test]
=> <ActionController::Parameters {"name"=>"あ", "course_id"=>"2"} permitted: false>

戻った時にはパラメーターは送られています。(text_fieldには値が入っているからそりゃ送られているよね)

デバッグ結果をみた時に思ったのは、params[:test]がある場合にcourse.idと戻るボタンを押した時に送られてきたcourse_idが一致すれば入力画面で選択したラジオボタンにセットされるんじゃないかと仮定して次のコードを書きました。

  - Course.where(id: 1..3).each_with_index do |course, i|
    - if params[:test]
   # 戻るボタンを押した際に選択したラジオボタンに値をセットする
      = f.radio_button :course_id, "#{course.id}", checked: "#{course.id == params[:test][:course_id]? 'checked' : '' }", id: "course-#{course.id}"
      %label.radio{for: "course-#{course.id}"}= course.fee_i18n
    - else
      = f.radio_button :course_id, "#{course.id}", checked: "#{i == 0 ? 'checked' : '' }", id: "course-#{course.id}"
      %label.radio{for: "course-#{course.id}"}= course.fee_i18n

これでいける!と思い試してみます。

Image from Gyazo

だめじゃん!

なんでや!

もう一回デバッグ結果をみてみます。

 pry(#<#<Class:0x00007f8f77e3efc8>>)> params[:test]
=> <ActionController::Parameters {"name"=>"あ", "course_id"=>"2"} permitted: false>

うん?

送られてきたcourse_idは文字列として送られてきているのか。

同じ数字でもcourse.idは数値でcourse_idは文字列扱いだから比較ができなくて選択したラジオボタンにセットされないのか

ということでcourse_idを数値化します。

 - if params[:test]
    # params[:test][:course_id]を数値化する
    = f.radio_button :course_id, "#{course.id}", checked: "#{course.id == params[:test][:course_id].to_i ? 'checked' : '' }", id: "course-#{course.id}"
    %label.radio{for: "course-#{course.id}"}= course.fee_i18n
 - else
    = f.radio_button :course_id, "#{course.id}", checked: "#{i == 0 ? 'checked' : '' }", id: "course-#{course.id}"
    %label.radio{for: "course-#{course.id}"}= course.fee_i18n

これで試してみます。

Image from Gyazo

できてる!

このようにして戻るボタンを押したら選択したラジオボタンに値がセットできるようになりました。

もう一度完成のコードを載せます。

= form_for @test, url: confirm_test_path do |f|
  = f.label :name
  %br/
  = f.text_field :name, placeholder: "名前を入力してください"
  - Course.where(id: 1..3).each_with_index do |course, i|
    - if params[:test]
     # 戻るボタンを押した際に選択したラジオボタンに値をセットする
      = f.radio_button :course_id, "#{course.id}", checked: "#{course.id == params[:test][:course_id].to_i ? 'checked' : '' }", id: "course-#{course.id}"
      %label.radio{for: "course-#{course.id}"}= course.fee_i18n
    - else
     # 初めて入力画面に移動した時に適用される
      = f.radio_button :course_id, "#{course.id}", checked: "#{i == 0 ? 'checked' : '' }", id: "course-#{course.id}"
      %label.radio{for: "course-#{course.id}"}= course.fee_i18n
  = f.submit "確認画面に進む"

まとめ

ラジオボタンをeachで設置して戻るボタンを押した時に選択したラジオボタンに値をセットする方法

・ビューで戻るボタンを押した時のパラメーターがあるかどうかを条件設定する。

valueと比較したいパラメーターの値を条件設定してvalueとパラメーターが一致していれば、そのラジオボタンに値がセットされる。

感想

ビューにロジックを書いてしまったけど、DecoratorかConcernに書いたほうがいいような気がするし、もっといい書き方があるとは思うけど思いつかないのが残念。。。

あとはform_withで書いていないし、labelもf.labelで合わせてないのが綺麗じゃないので、綺麗に書けるようにしてまたコードは編集し直そうと思います。