タイトル通りなんですが、

  1. 通常のフォームで submit する
  2. 確認画面に遷移
  3. 「戻る」を選ぶと 1. に戻る
  4. 「確定」を選ぶと「完了」画面に飛ぶ

みたいな「うわぁ…」って感じの Form を実装する機会があり、なるべく綺麗に書くにはどうすればいいかなーってのを調べながら実装したのでメモを残しておきます。

未だに納得いってないし、基本こういうアホみたいな画面遷移の実装は断りたい。

Dependencies

  • Rails5.1.0.rc1
  • Ruby 2.4.1

今回は記事の投稿 form で考えます。

posts テーブルがあって、シンプルに title, body があるとしましょう。

モデルクラスの実装は以下のような感じ

class Post < ApplicationRecord
  validates :title, presence: true
  validates :body,  presence: true
end

サンプルコード

エンドポイント

  • a: GET /posts/new
  • b: POST /posts/confirm
  • c: POST /posts
  • d: GET /posts/complete

フローとしては

  1. a -> b に request -> (validation 通る) -> b の view -> 確定 -> d
  2. a -> b に request -> (validation 失敗) -> a
  3. a -> b に request -> (validation 通る) -> b の view -> 戻る -> a

の 3 パターンあると思ってください

$ bin/rails g controller Posts new confirm complete

new

普通に実装

class PostsController < ApplicationController
  def new
    @post = Post.new
  end
end

app/views/posts/new.html.haml

%h1 Create a new post

= form_with model: @post, url: { action: :confirm } do |f|
  = f.label :title
  = f.text_field :title

  = f.label :body
  = f.text_field :body

  = f.submit 'Confirm'

:url オプションを渡すあたりがミソでしょうか。

confirm

class PostsController < ApplicationController
  before_action :load_post, only: [:confirm]

  # TODO
  skip_before_action :verify_authenticity_token, only: [:confirm]

  def confirm
    render :new unless @post.valid?
  end

  private

  def load_post
    @post = Post.new(post_params)
  end

  def post_params
    params.require(:post).permit(:title, :body)
  end
end

@post が validation 通らなければ new を表示、通れば confirm.html.haml を rendering します

自分の環境だと ActionController::InvalidAuthenticityToken が起きたので一旦ここでは skip してあります…

app/views/posts/confirm.html.haml

%h1 Confirm your post

= form_with model: @post do |f|
  = f.label :title
  = f.text_field :title, readonly: true

  = f.label :body
  = f.text_field :body, readonly: true

  = f.submit 'Submit'
  = f.submit 'Back'

確認画面では編集されると怖いので :readonly をつけてあります。

submit が 2 つありますが、ここで渡してある文字列( SubmitBack ) が controller に渡されるのでそれを利用して処理を振り分けます

create

class PostsController < ApplicationController
  # :create も追加
  before_action :load_post, only: [:create, :confirm]

  skip_before_action :verify_authenticity_token, only: [:create, :confirm]

  SUBMIT_BUTTON = 'Submit'

  def create
    if submit_clicked?
      @post.save!
      redirect_to complete_posts_path
    else
      render :new
    end
  end

  private

  def submit_clicked?
    params[:commit] == SUBMIT_BUTTON
  end
end

create は view ありません

complete

特筆すべきことはないですね。ただの GET で view を表示するだけなので

終わり

仕事のコードベースでは verify_authenticity_token を skip しなくても大丈夫だったんですが、ちゃんと調べた方がよさそうですね… :sweat_smile:

References

日記

ついでに近況をば。

1~2月は Wii で出たゼノブレイドを WiiU で やったりしてました。100時間近くプレイしたのはこの時が初めてでしたが結構楽しかったです。

正直途中くらいまでは「ムカつくなー」って思いながらプレイしてましたが、ある程度まで進むとストーリーが気になってのめり込んでました。

3月入ってからは Switch にかぶりついてました。ゼルダ1-2-switch をとりあえず買って、後者は一回しかプレイしてないですが、前者は未だにちょいちょいやってます。

あとは チョキっとスニッパーズ が結構面白くて、二人でやるのにオススメですね。これもちょいちょいやってます

雪のセツナ は PSVita と PS4 で前に出てたみたいですが、Vita でちょっとレビューが荒れていたので Amazon のレビューが書かれるまで待ってました。結局中々レビュー書かれなかったので twitter 検索で評判見てから買いましたが。

絵が好きな感じだったので買ったんですが、ほんとに一本道の RPG なので割と退屈な感じです。レベルもあんまりサクサクは上がらないのでちょっと期待外れでした。

来月末にはマリオカート出るのでまたそれで時間潰そうと思ってます。

そろそろ ARMS, イカ2 の発売日公開してほしい…

イカ2 の試射会は意外と勝ち越せました。社内のイカやってた人にテクニック教えてもらったりしてます。

Switch いいですね。久しぶりにハードに対して「欲しい!」って思えたので結構興奮してます。

今はまだタイトルが少ないので、これからどんどん増えてほしい…

そんなところで :wave: