そもそもウィザード形式フォームとは
❌縦に長いフォーム画面
⭕️複数画面に順に遷移していくフォーム画面
実装方法
開発環境
結論
以下の記事を参考にして実装しました https://qiita.com/NT90957869/items/56ca4101d7ba37778076 https://qiita.com/NT90957869/items/614842934feff9812203
上記の記事との違いは、Deviseとsessionを組合わせて実装(バリデーションはRailsのモデルに記載)したことです。
記事では、Deviseが使えないとなっていますが、コントローラをDeviseから完全分離させて実装し、ログイン・ログアウト・カレントユーザーのデータ取得などはDeviseを利用するといういいとこ取りをしています。
sessionを用いることで、全ての情報をユーザーが入力した後に、一括でDBに保存することができます。
逆にいうと、途中でユーザーがフォームの入力を辞めた時には、入力データがDBに保存されないので、中途半端なデータが残らない仕様にできます。
補足
他にも以下のような実装方法もあるようです
- gem wickedを利用する(ただし、画面遷移ごとにDBにデータが保存される模様)
- JSで画面を切り替える(つまり、画面遷移ではなく、見た目を変える)
コード
下記の記事がわかりやすいです。 https://qiita.com/NT90957869/items/614842934feff9812203
今回は、全体の流れと、上記の記事と違う部分をピックアップして説明していきます。
- Deviseの導入
- Deviseとは関係のないコントローラの作成
- ルーティングの設定
- フォームのマークアップ
- sessionを使ったデータの保持
- 最後にsessionのデータを使って、一括登録
- 一括登録後、自動的にサインインさせる
Deviseの導入
Gemfileに下記を記載
gem 'devise'
以下をコンソールで実行
$ bundle install #gemのインストール $ rails g devise:install #deviseを使うのに必要なファイルの生成 $ rails g devise user #deviseでUserモデルの生成
マイグレーションファイルが生成されるので、Usersテーブルに必要なカラムを指定しマイグレートを実行。
※ もともと記載されているdevise関連の記述は変更しなくて大丈夫です。
※ passwordカラム、password_confirmationカラムは作成しないでください。左記の記述はビューのフォームで使用しますが、実際のパスワードはdeviseで暗号化され、encrypted_passwordカラムに自動的に保存されます。
(具体例)
# frozen_string_literal: true class DeviseCreateUsers < ActiveRecord::Migration[5.2] def change create_table :users do |t| ## Database authenticatable t.string :nickname, null: false t.string :email, null: false, unique: true t.string :last_name, null: false t.string :first_name, null: false t.string :last_name_kana, null: false t.string :first_name_kana, null: false t.integer :birthdate_year, null: false t.integer :birthdate_month, null: false t.integer :birthdate_day, null: false t.integer :phone_number, null: false, unique: true t.string :address_last_name, null: false t.string :address_first_name, null: false t.string :address_last_name_kana, null: false t.string :address_first_name_kana, null: false t.string :address_number, null: false t.integer :address_prefecture, null: false, default: 0 t.string :address_name, null: false t.string :address_block, null: false t.string :address_building t.integer :address_phone_number t.text :introduce t.string :encrypted_password, null: false, default: "" ## Recoverable t.string :reset_password_token t.datetime :reset_password_sent_at ## Rememberable t.datetime :remember_created_at ## Trackable # t.integer :sign_in_count, default: 0, null: false # t.datetime :current_sign_in_at # t.datetime :last_sign_in_at # t.string :current_sign_in_ip # t.string :last_sign_in_ip ## Confirmable # t.string :confirmation_token # t.datetime :confirmed_at # t.datetime :confirmation_sent_at # t.string :unconfirmed_email # Only if using reconfirmable ## Lockable # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts # t.string :unlock_token # Only if unlock strategy is :email or :both # t.datetime :locked_at t.timestamps null: false end add_index :users, :email, unique: true add_index :users, :reset_password_token, unique: true # add_index :users, :confirmation_token, unique: true # add_index :users, :unlock_token, unique: true end end
$ bundle exec rake db:migrate
Deviseとは関係のないコントローラの作成
今回は、signup_controller.rbを作成しました。
$ bundle exec rails g controller signup
ルーティングの設定
必要なビューの数(遷移させたい数)だけ、ルーティングを設定してください。
(具体例)
resources :signup do collection do get 'step1' get 'step2' get 'step3' get 'step4' # ここで、入力の全てが終了する get 'done' # 登録完了後のページ end end
フォームのマークアップ
遷移ページ分だけビューを用意してください。
e.g.) step1.html.haml, step2.html.haml, step3.html.haml, ...etc
各ページのフォームタグのurlで、上記で設定したルーティング(次のページへのパス)を記載します。
(具体例)
= form_for @user, url: step2_signup_path, method: :get, html: {class: 'first-main__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文字以上'
= form_for @user, url: step3_signup_path, method: :get, html: {class: 'second-wrapper__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: '例) アヤ'
sessionを使ったデータの保持
Railsではsessionという情報保管方法があります
Railsドキュメント
http://railsdoc.com/references/session
概略としては、キーバリュー形式で、任意のデータを保管できる仕組みです。
# 保管方法 session[キー] = 値 # 具体例 session[:nickname] = "hoge" # キー":nickname"にバリュー"hoge"を保管 session[:nickname] -> "hoge"
sessionを利用して、各ページのデータを遷移ごとに保管していきます。 やるべきことは、以下の2つです。
- ストロングパラメータの使用
- 各アクションでsessionにparamsデータを保管
private # 許可するキーを設定します def user_params params.require(:user).permit( :nickname, :email, :password, :password_confirmation, :last_name, :first_name, :last_name_kana, :first_name_kana, ~省略~ ) end
class SignupController < ApplicationController # 各アクションごとに新規インスタンスを作成します # 各アクションごとに、遷移元のページのデータをsessionに保管していきます def step1 @user = User.new # 新規インスタンス作成 end def step2 # 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 step3 # 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 ~遷移ページ分繰り返す~
最後にsessionのデータを使って、一括登録
データの入力が全て終了するページで、フォームのurlをcreateアクションが実行されるように記載します。
(具体例)
= form_for @user, url: signup_index_path, method: :post, html: {class: 'forth-main-wrapper__box'} do |f|
createアクション内で、今まで保管したsessionのデータを渡し、DBに保存します。
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
一括登録後、自動的にサインインさせる
deviseのメソッドsign_inを活用し、createアクションで作成・保存したデータのidを用いてサインインさせます。
def done sign_in User.find(session[:id]) unless user_signed_in? end
まとめ
以上の方法を使えば、問題なく登録できるかと思います。
deviseを導入しているので、サインインやログアウト、ログインユーザーのデータ取得、サインイン中かどうかの判断など、全てdeviseのメソッドを活用できる点がいいところです。
他テーブルも含めた一括登録もできます。以下の記事を参考にしてみてください。
https://qiita.com/NT90957869/items/614842934feff9812203
注意点
上記の実装のままでは、ページ遷移ごとにバリデーションをチェックできません。 ページ遷移前のパリデーションのチェック方法はまた別記事にまとめたいと思います。