#はじめに
閲覧いただきありがとうございます。
よわよわエンジニアです。
僕みたいなよわよわエンジニアでもわかるように記載していきたいです。
##今回のテーマと環境
Railsでウィザードフォームを実装しました。
Rails 5.2.4
Ruby 2.5.1
gem 'devise'
デバイスのインストールとモデル作成まではいってると仮定します。
##ウィザードフォームとは?
入力項目が多い際にページ遷移しながら
1ページごとに入力された情報を保持して、全ての入力が終わってから最後にその情報を送信し、
DBに保存するフォームのこと。
要するに入力項目が100個あったとして、
1ページで100個入力するよりも、1ページ10個x10ページ作っちゃおうよというものです。
パッとみた時に
入力項目が限られているので認識しやすいが、何ページもあって萎えちゃうっていうデメリットもある。
###ウィザードの動きの原理
通常なら1ページに入力したフォームの内容は、2ページ目にaやlink_toでページ遷移すると、
入力内容が消えてしまいますが
sessionという物を使い、入力内容を保持したままで
_form_の_submit_を押すことでページ遷移し、全ての入力が終わった段階で_create_を走らせてDBに内容を保存します。
##いざ実装
###デバイスとは関係ないコントローラーを作る
まずはコントローラーを作ります。
signupなど、わかりやすい名前で作っちゃいましょう。
$rails g controller signup
###ルーティングの設定
前述のとおり今回実装するウィザードは、
sessionといもので情報を保持し、次のページへ進まなければいけません。
今回はcollection do
で必要な数だけページのルーティングをしましょう。
resources :signup do
collection do
get 'page1'
get 'page2'
get 'page3' # ここで全て入力完了。このページの送信ボタンを押した後にcreateが走る
get 'done' # 登録完了後のページ
end
end
###フォームを作る
各ページ入力したいフォームを作ります。
この時に、先ほど設定したルーティングの数だけページを新規作成しましょう。
その際のURLの送信先に注意!!
= form_for @user, url: page2_signup_path, method: :get, html: {class: 'form__box'} do |f|
= f.text_field :nickname, placeholder: '例) メルカリ太郎'
= f.email_field :email, placeholder: 'PC・携帯どちらでも可'
= f.password_field :password, placeholder: '6文字以上'
= f.password_field :password_confirmation, placeholder: '6文字以上'
= f.submit #これでpage2へ進む。link_toなどで遷移すると情報が消えちゃうので注意
= form_for @user, url: page3_signup_path, method: :get, html: {class: 'form__box'} do |f|
= f.text_field :last_name, placeholder: '例)鬼頭'
= f.text_field :first_name, placeholder: '例)権蔵'
= f.text_field :last_name_kana, placeholder: '例)オニガシラ'
= f.text_field :first_name_kana, placeholder: '例)ゴンゾウ'
= f.submit #これでpage3へ進む。link_toなどで遷移すると情報が消えちゃうので注意
form_for @モデル名, url: 次のページのアクションが動くprefix, do |f|
と記載してください。(クラス名はお好みで)
###session登場
Railsにはsessionというものがあります。
キーバリュー形式で情報を保管できるよー。ってやつです。
保管方法は
session[:キー] = 値で保管できます。以下サンプル。
session[:name] = "田中"
#この場合、:name キーに value "田中" を保管できる。
#binding.pryなどで保管できているか確認するときは session[:name] と入力すると内容が表示される。
このsessionで入力したものを保持しながら、formのsubmitで次のページへ送り、保持した状態でcreateかけると保存できます。
##コントローラーの設定
class SignupController < ApplicationController
# 各アクションごとに新規インスタンスを作成します←ここ重要
# 各アクションごとに、遷移元のページのデータをsessionに保管していきます
def page1
@user = User.new # 新規インスタンス作成
end
def page2
# step1で入力された値をsessionに保存
session[:nickname] = user_params[:nickname]
session[:email] = user_params[:email]
session[:password] = user_params[:password]
session[:password_confirmation] = user_params[:password_confirmation]
@user = User.new # 新規インスタンス作成
end
def page3
# step2で入力された値をsessionに保存
session[:last_name] = user_params[:last_name]
session[:first_name] = user_params[:first_name]
session[:last_name_kana] = user_params[:last_name_kana]
session[:first_name_kana] = user_params[:first_name_kana]
@user = User.new # 新規インスタンス作成
end
~省略。遷移ページ分繰り返す~
private
# 受け取り許可するキーを設定します
def user_params
params.require(:user).permit(
:nickname,
:email,
:password,
:password_confirmation,
:last_name,
:first_name,
:last_name_kana,
:first_name_kana,
~省略。各pageで入力し、受け取る値を設定した分だけ受け取れるようにしておく~
)
end
ポイントは、
page1で入力した情報を、page2アクションが呼び出されるときにsession代入すること。
(理由)
x間違い
def page1
session[:nickname] = user_params[:nickname]
session[:email] = user_params[:email]
session[:password] = user_params[:password]
session[:password_confirmation] = user_params[:password_confirmation]
@user = User.new # 新規インスタンス作成
end
上記のようにpage1が呼び出されるアクションで代入してしまうと、
page1を開いた時にはフォームに情報が入力されていないのに「何を代入するんじゃ、userの情報なんでないぞ小僧」と叱られちゃいます。
また、新規新スタンスを作るのを忘れずに。
##createを走らせる方法
$rails routes
を実行して、createアクションが走るパスを探してください。
signup_index GET /signup(.:format) signup#index
POST /signup(.:format) signup#create
こんな感じででてくるかと思われます。
今回ですと、signup_indexのPOSTの横にsignup#createがあるので
パスとしてはsignup_index method: :post
ですね。
= form_for @user, url: signup_index_path, method: :post, html: {class: 'form__box'} do |f|
~省略~
= f.submit #これでcreateが走る。
page1,page2との違いはurlとその直後のmethodです。
ここに先ほど探してきたパスを入れていますね。その結果createが動きます。
###createの設定
def create
@user = User.new(
nickname: session[:nickname], # sessionに保存された値をインスタンスに渡す
email: session[:email],
password: session[:password],
password_confirmation: session[:password_confirmation],
last_name: session[:last_name],
first_name: session[:first_name],
last_name_kana: session[:last_name_kana],
first_name_kana: session[:first_name_kana],
~省略~
)
if @user.save
# ログインするための情報を保管
session[:id] = @user.id
#データベースに保存できた場合どこに飛ぶか?
redirect_to done_signup_index_path
else
#データベースに保存ができなかった場合どこに飛ぶか?
render '/signup/registration'
end
end
今まで保持してきた値をインスタンスに渡してあげます。
なので、ここはsessionの数だけ記載が必要です。
これにより、今までsession君が保持していたものをインスタンスに渡し、全てのパラメーターをもったインスタンスが誕生し、クリエイトが走ることにより、DBに登録できるという流れです。
##自動ログイン
def done
sign_in User.find(session[:id]) unless user_signed_in?
end
最後に、デバイスのsign_inメソッドに モデル名.findで保存したデータのidを用いてサインインさせます。
これで、doneアクションが呼び出されて自動的にサインインさせることができます。
##さいごに
いかがだったでしょうか?
この通りに設定していけば動くはずです。
ただ、現在の問題点としては、
①1つのモデルでしかフォームを作れない。
②バリデーションエラーでもページ遷移できてしまう。
これに関してはまた近いうちに追記しようと思いますが、
「今知りたいんだよ!このクソ雑魚エンジニアが!」って人は下記記事を参照してもらえればわかりやすいと思います。
■他テーブルの情報も一括保存 + バリデーション
https://qiita.com/NT90957869/items/614842934feff9812203
むしろこの記事だけ見てもいいかも・・・・(根も葉もない)
以上、ご覧いただきありがとうございました。