27
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

redirect_toとrender, flashとflash.nowの違い

Last updated at Posted at 2020-03-29

ポートフォリオ作成中、バリデーションのエラーメッセージが表示できずに苦戦してました
redirect_toとrenderの違いを理解していなかったのが原因です。
redirect_toとrender, ついでにflashとflash.nowの違いについても今更理解したのでまとめます。

参考

renderとredirect_toの違い

  • render => ビューを描画するだけで、新たにhttpリクエストを送信するわけではない
  • redirect_to => 新たにhttpリクエストを送信して、その結果コントローラからビューが描画される
app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
  def new
    @tweet = Tweet.new
  end

  def create
    @tweet = Tweet.new(tweet_params)
    if @tweet.save
      # 成功時の処理
    else
      flash[:danger] = '失敗しました'
      redirect_to new_tweet_path
    end
  end

  private

    def tweet_params
      params.require(:tweet).permit(:content)
    end
end
app/views/tweets/new.html.slim
= form_with model: @tweet, local: true do |f|
  - if @tweet.errors.any?
    .alert.alert-danger
      ul
        - @tweet.errors.full_messages.each do |message|
         li= message
  = f.label :content
  = f.text_area :content
  = f.submit 'tweet'

こんな感じの設定があったとします(tweetモデルはpresence: trueのconent: stringカラムを持つ)
この場合、空白で送信してもエラーメッセージは表示されません
redirect_toでリダイレクトするとコントローラを経由してしまうため、新たに@tweetが生成され、エラーメッセージを含んだ@tweetが消えてしまうから…だと考えています

ここで、コントローラを経由せずにページを描画するrenderを使用します

app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
  def new
    @tweet = Tweet.new(tweet_params)
  end

  def create
    @tweet = Tweet.new(tweet_params)
    if @tweet.save
      # 成功時の処理
    else
      flash[:danger] = '失敗しました'
      render action: :new # ここを変更
    end
  end

  private

    def tweet_params
      params.require(:tweet).permit(:content)
    end
end

renderを使用すればコントローラを経由せずにページを描画することができるため、エラーメッセージを含んだ@tweetがビューに渡されif @tweet.errors.any?に引っ掛かり、エラーメッセージを表示することができます

これで無事エラーメッセージを表示することができました!

flashとflash.nowの違いについて

  • flash => 次のリクエスト終了時まで表示される
  • flash.now => 次のリクエスト開始時まで表示される

コントローラーが上の最終状態のまま空白で送信した場合、失敗しましたというflashメッセージが表示され、失敗します。
しかし、このままでは次別のページに遷移した時flashが残ったままになってしまいます。

renderを使うとリクエストを送信しないため次にリクエストを送信した時もflashが残り、その次のリクエストを送信した時点でやっとflashが消えるからだと思われます

そこでflash.nowを使ってみましょう

app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
  def new
    @tweet = Tweet.new(tweet_params)
  end

  def create
    @tweet = Tweet.new(tweet_params)
    if @tweet.save
      # 成功時の処理
    else
      flash.now[:danger] = '失敗しました' # ここを変更
      render action: :new
    end
  end

  private

    def tweet_params
      params.require(:tweet).permit(:content)
    end
end

flash.nowは次リクエストを送信した時点で消えるため、これでflashを残さずに次のページへ移動することができます

それぞれの組み合わせ

  • flash[]とredirect_to => 次のページにリダイレクトした時点でflashは消える
  • flash[]とrender => renderはリクエストを送信しないため、次のページに移動してもflashは残る
  • flash.now[]とredirect_to => redirect_toの時点でflashが消えるため、flash自体表示されない
  • flash.now[]とrender => 次のページへリダイレクトした時点でflashは消える

最後に

Railsチュートリアルで丁寧に説明されていた箇所なんですが、実際に自分で試行錯誤して初めて理解できました

何かおかしい部分があれば指摘して頂けると嬉しいです

読んでいただきありがとうございました!

27
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?