4
4

More than 3 years have passed since last update.

[Rails] render後にURLが変わってしまうことへの対処法

Last updated at Posted at 2020-10-30

前提

環境は以下の通りです。
Mac OS Catalina 10.15.7
Ruby 2.7.1
Rails 6.0.3.3

自分の備忘録もかねて、できるだけわかりやすく記録しようと思います。
問題をわかりやすく示すためにここでは例としてRailsチュートリアルを用います。

具体的には第7章でユーザー登録の機能を実装する際、バリデーションに引っかかって登録失敗後フォーム画面にrenderされるにあたり、URLが/signupから/usersにURLに変わってしまうという問題、つまり

URLがlocalhost:3000/signupからlocalhost:3000/usersに変わってしまう
→usersに対応するviewを作っていないためリロードするとエラーが出てしまう

という問題について解決方法を解説します。

原因

createアクションでユーザー登録に失敗した時にrender 'new'するということはnewアクションを呼ばず、viewだけを切り替えているということです。
createアクションは/usersなのでアクション後は当然URLは/usersになります。
このURLと画面の不整合が問題になるというわけです。

解決方針

controllerのコードなどは変えずにjavascriptでURLを変えることで解決します。対象のviewのみで特定のjsを読み込みます。
/usersから/signupに変更、つまりアクション後も一見/signupのままURLが固定されたように見せる。)

解決方法

手順

  1. javascriptを作成
  2. 対象のview(ユーザー登録のview)でjavascriptを使えるようにする
  3. 対象のviewのみにタグを埋め込む

1. jsファイル作成

app/javascript/packs以下の任意のディレクトリもしくは直下任意の名前(ここではusers/signup_render.js)で下記のjsを作成。

app/javascript/packs/users/signup_render.js
history.replaceState('', '', '/signup')

以下を参考にしました。
replaceStateについてわかりやすい解説
replaceStateについて詳しく

history.replaceState()メソッドで現在の履歴を編集し、メソッドに渡されたURLパラメータによって/users/signupに置き換えています。

2. jsを使えるように

対象のview用にコンパイルを行うために、新しいコンパイル用の設定を作成します。app/javascript/packs/application.jsに追記してしまうと全てのページに反映されてしまい都合が悪いので、新たにエントリポイントをして必要なページから呼び出すようにします。
ここでは呼び出すファイルと同じくsignup_renderという名前にしました。

app/javascript/packs/users/signup_render.js

require("users/signup_render")

パスはapp/javascriptを起点として記載します。
このあたりのフォルダ構成などは こちらの説明 がとてもわかりやすかったのです。

3. viewにタグの埋め込み

対象のviewにjavascript_pack_tagを埋め込みます。チュートリアルの例ではリスト7.20のユーザー登録画面です。

app/views/users/new.html.erb

 <!-- これが元々のコード ↓ -->

<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_with(model: @user, local: true) do |f| %>
      <%= render 'shared/error_messages' %>

      <%= f.label :name %>
      <%= f.text_field :name, class: 'form-control' %>

      <%= f.label :email %>
      <%= f.email_field :email, class: 'form-control' %>

      <%= f.label :password %>
      <%= f.password_field :password, class: 'form-control' %>

      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation, class: 'form-control' %>

      <%= f.submit "Create my account", class: "btn btn-primary" %>
    <% end %>
  </div>
</div>

<!-- 下にこれを追記↓ -->
<% if @user.errors.any? %>
  <%= javascript_pack_tag 'users/signup_render' %>
<% end %>

if @user.errors.any?で条件を設定しているのは
「バリデーションエラーが発生 = ユーザー登録失敗してrenderされる時だけjsの処理をしてね」ということです。

<%= javascript_pack_tag 'users/signup_render' %>は「app/javascript/packs/users/signup_render.jsを読み込んでね。」という意味です。

以上、参考になれば幸いです。

その他参考ページ

Rails5 render 'new' 後にURLが変化する
【Rails】renderを使った時のリロード対策
newアクションのバリデーションエラー後のURLの挙動とそれに伴うエラーについて
Ruby on RailsにおけるJavascriptファイルの取り扱い(Rails6)

4
4
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
4
4