【Ruby on Rails】確認画面の実装

# はじめに
createアクションでインスタンスを生成する際に、いったん確認画面を挟んで修正点がなければcreateアクションを実行するという仕様のアプリを作成したので備忘録としてまとめたいと思います！

[実装イメージ](https://gyazo.com/468c88df65eaf904c6344126f68d0869)

イメージGifのように
① 各項目を入力して「確認へ」を押す
↓
② 確認画面で確認したら「送信」を押す（修正箇所があれば入力画面に戻る）
↓
③ 完了画面へリダイレクト

この流れでインスタンスを保存していきます。

# 前提（開発環境）

*  **macOS Catalina**
*  **Ruby 2.6.5**
*  **Ruby on Rails 6.0.0**
*  **Visual Studio Code**
*  **データベース: Mysql**
*  **テンプレートエンジン: haml**

# 目次
[1.ルーティング](#1-ルーティング)
[2.モデル](#2-モデル)
[3.コントローラー](#3-コントローラー)
[4.ビュー](#4-ビュー)

## 1. ルーティング
今回はeventというインスタンスを作成するという設定で実装を進めます。
そのためER図は以下の通りです。
![スクリーンショット 2020-12-22 18.35.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/772095/8ea38c00-7eae-a714-c2b5-650351a65f18.png)
イベントモデルがユーザーモデルに紐付いており、イベントモデルのカラムには全てバリデーションを設定しています。
このeventモデルはpostでもtweetモデルでも任意のもので構いません。

それでは、本題に入ります。

```php:routes.rb
Rails.application.routes.draw do

  root to: 'events#index'

  resources :events, only: [:index, :new, :create, :show] do
    collection do
      post :confirm
    end
  end
end
```
通常は`new`でイベント作成画面に遷移して`create`すればイベント一覧に遷移するという仕様ですが、
今回は確認画面を挟むため`new`→`confirm`→`create`の順にアクションを実行します。
独自のアクションを設定する際は個別のidが必要ない場合はこのように`collection`をネストさせます。

## 2. モデル

```php:app/model/event.rb
class Event < ApplicationRecord
   with_options presence: true do
     validates :event_name
     validates :datetime
     validates :prefecture
     validates :place
     validates :detail
   end
end

# 空のバリデーションを設定
```

## 3.　コントローラー

```php:app/controllers/events_controller.rb
class EventsController < ApplicationController
  def new
    @event = Event.new
  end

  def confirm
    @event = Event.new(event_params)
    if @event.invalid? #入力項目に空のものがあれば入力画面に遷移
      render :new: 
    end
  end

  def create
    @event = Event.new(event_params)
    @event.user_id = current_user.id
    if params[:back] || !@event.save #戻るボタンを押したときまたは、@eventが保存されなかったらnewアクションを実行
      render :new and return
    redirect_to root_path
  end

  def show
    @event = Event.find_by(id: params[:id])
  end

  private

    def event_params
      params.require(:event).permit(:event_name, :datetime, :prefecture, :place, detail)
    end
end
```
ポイントは、アクションを移動する度にインスタンス情報の入ったパラメーターを渡しているところです。
ここでいう`Event.new(event_params)`のところです。ここに全てのインスタンス情報が入っているので、`new`→`confirm`に遷移しても情報が保持されます。

そして戻るボタンを押した時の挙動も、params[:back]で制御しています。backが渡ってきたら前の画面にrenderすればOKです。

## 4.　ビュー

### new画面

```php:events/new.html.haml
= form_with(model: @event, url: confirm_events_path(@event), local: true, id: "new-event") do |f|
      .top-area
        .create-item-1
          = f.label :event_name, "イベント名", class: "Form-label"
          = f.text_field :event_name,  maxlength: "50", class: "name-field"

      .middle-area
        .create-item-2
          = f.label :datetime, "開催日時", class: "Form-label"
          = f.date_field :datetime, class: 'form-control', class: "middle-field"

        .create-item-2
          = f.label :tag_list, "タグ付け"
          = f.text_field :tag_list, value: @event.tag_list.join(','), placeholder: "ダンス,東京,・・・", class: "middle-field"
         

      .bottom-area
        .create-item-3
          = f.label :prefecture, "開催場所（都道府県）", class: "Form-label"
          = f.select :prefecture, Event.prefectures.keys, {}, class: "place-field"

        .create-item-3
          = f.label :place, "開催場所（詳細）", class: "Form-label"
          = f.text_field :place,  maxlength: "30", class: "place-field"
      
      .create-item-4
        = f.label :detail, "イベント詳細説明", class: "Form-label"
        = f.text_area :detail,, class: "detail-area"

      .create-btn-field
        = f.submit "確認画面へ", class: "create-btn"
```
ポイントは, URL指定でconfirm_events_pathへリクエストを投げていることです。
有効であればconfirmアクションへ移動して次の確認画面ではEvent.new(user_params)が表示されるので状態が維持されます。
パスの指定のhamlの記述は自分好みでカスタマイズしてください。

### confirm画面

```php:events/confirm.html.haml
%h2.please-check
  以下の内容を確認してください
    
    .confirm-contents
      .confirm-contents__item
        %p.item-heading
          イベント名：
        %p.item-content
          = @event.event_name
      .confirm-contents__item
        %p.item-heading
          開催日時：
        %p.item-content
          = @event.datetime.strftime("%Y年%-m月%-d日")
      .confirm-contents__item
        %p.item-heading
          開催場所（都道府県）：
        %p.item-content
          = @event.prefecture
      .confirm-contents__item
        %p.item-heading
          開催場所（詳細）：
        %p.item-content
          = @event.place
      .confirm-contents__item
        %p.item-heading
          タグ：
        %p.item-content
          = @event.tag_list
      .confirm-contents__item
        %p.item-heading
          イベント詳細：
        %p.item-content
          = @event.detail
      
    = form_with(model: @event, local: true, id: "confirm-event") do |f|
      = f.hidden_field :event_name
      = f.hidden_field :datetime
      = f.hidden_field :prefecture
      = f.hidden_field :place
      = f.hidden_field :detail
      = f.hidden_field :tag_list
      = f.submit "作成", class: "complete-btn"
      = f.submit "修正", name: :back, class: "back-btn"
```
ポイントは2つです。

### **①new画面のformの項目をhiddenにしておく**
new画面でのインスタンスをもっていきたいので、`confirm`のviewでもformを記述します。
しかし、formが見えてお邪魔なので、hiddenにしておきます。

### **②修正ボタンの設定**
戻るボタンはnameでパラメータを指定して、アクション側で存在すれば前の画面にrenderしてあげます。

無事インスタンスが保存されれば、イベント一覧画面にリダイレクトされます！

# おわりに
さいごまで読んでいただきありがとうございました！
お疲れさまでした。。

# 参考文献
[【Rails】インスタンスの状態を保ちながら入力 → 確認画面 → 保存 を実装する方法](https://qiita.com/ngron/items/d55aac6e81a9fb2fe81c)