4
3

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.

renderするとURLが変わり、リロード問題に直面するやつ

Posted at

#はい!僕です!!

railsでポートフォリオを作成しています。

表題の問題にぶち当たり半日消耗したので、記録しておく。

#環境
ruby2.7
rails6.0

#問題の詳細

例えばblogs_controllerのcreateアクションを実装するとして、バリデーションが失敗するとrenderで新規投稿画面をrenderするコードを書きます。

blogs_controller.rb

 def create
    @blog = current_user.blogs.build(blog_params)
    if @blog.save
      flash[:success] = "投稿完了!!"
      redirect_to @blog
    else
      render "new"
      flash.now[:alert] = "失敗しました"  
    end
  end

投稿が失敗するとフラッシュメッセージとともにnew画面が表示されます。

ここでリロードするとルーティングエラーかもしくは別画面が表示されます。

#なぜか?〜renderは画面の描写だけ
例の流れは

①新規作成画面のURLはblogs/new
②投稿するとcreateアクションが作動する。
③投稿が失敗する
④renderで画面はnewだがrenderは画面を描写しているだけなので、URLはcreateアクションのURLblogsになっている。
⑤リロードやF5とか押しちゃうと、blogsをgetしちゃうのでindexアクションが動く。(ルーティングを設定してなかったら、ルーティングエラーが発生)

rails.routesコマンド
blogs     GET      /blogs(.:format)                 blogs#index
          POST     /blogs(.:format)                  blogs#create
new_blog  GET   /blogs/new(.:format)     blogs#new
edit_blog GET  /blogs/:id/edit(.:format)   blogs#edit
                                                             
blog GET      /blogs/:id(.:format)         blogs#show
     PATCH    /blogs/:id(.:format)         blogs#update
     PUT      /blogs/:id(.:format)         blogs#update
     DELETE   /blogs/:id(.:format)         blogs#destroy

#解決策

①redirect_toを使う

renderを使っていることで起きているエラーなので、redirect_toを使う

blogs_controller.rb

 def create
    @blog = current_user.blogs.build(blog_params)
    if @blog.save
      flash[:success] = "投稿完了!!"
      redirect_to @blog
    else
      redirect_to new_blog_path
      flash.now[:alert] = "失敗しました"  
    end
  end

こうするとURLの問題は解決する。

ただ新たな問題としてフォームに入力していたものは消えてしまう。
(余談だけど、railsチュートリアルみたいにパーシャルを使ってエラーメッセージを描写している場合はそれも表示されなくなる)

この場合の対策はこちらの方の記事にあった
https://qiita.com/yuyasat/items/49e3296f3c64fccc7811

②JavaScriptを使う

僕はこの方法で解決した。

①新しいJavaScriptファイルを作成して、その中でhistory.replaceStateを使う。

app/javascript/blogs/render.js
history.replaceState('', '', '/blogs/new')

history.replaceStateとは

現在のブラウザの履歴を更新する。
第3引数でURLを入力する。

createアクションの失敗した際、上述したように、画面はviews/new、URL/blogsになっている。

この画面とURLの差異で問題が起きているので、現在のURL/blogs
blogs/newに変えます。

②applocation.jsに設定する
そのまま引用させていただきます。

対象のview用にコンパイルを行うために、新しいコンパイル用の設定をapp/javascript/packs/application.jsに作成する。

app/javascript/packs/application.js
.
.
.

require("blogs/render")

ちなみにコンパイルの言葉の意味がいまいちわかりません。
コンパイルってなんやねん・・・?

https://employment.en-japan.com/tenshoku-daijiten/14875/
コンパイルとは、プログラミング言語で書かれた文字列(ソースコード)を、コンピュータ上で実行可能な形式(オブジェクトコード)に変換することです。

・・・後回しで・・

③対象のviewに埋め込む

app/views/blogs/new.html.erb
<div class="container">
  <h2>新規作成</h2>
  <%= form_with model: @blog, local:true do |form| %>
  
  <%= render '/shared/error_messages', object: form.object %>

.
.
.
.

 <% end %>
</div>


# 下記を追加 

<% if @blog.errors.any? %>
  <%= javascript_pack_tag 'blogs/render' %>
<%end%>


これはバリデーションエラーがあったら、renderするときにjsを読み込んでねということを記述している。

読み込まれたjsは当然①のURLを変更するため、urlがblogs/newになる。

#課題
解決はしたけど、問題は諸々ある

①とりあえず初回投稿失敗時にめちゃくちゃ時間かかる。

②各リソースごとにjsファイルを用意しないといけない
他に方法はあるのかもしれないけど、ユーザー新規登録にも同様の実装をしており、バリデーションをかけるものすべてに、やるのはDRYじゃない気がする(ただの直感)

このあたりは今後考えていく。

#参考
https://laptrinhx.com/rails-render-houniurlga-bianwatteshimaukotoheno-dui-chu-fa-2616278188/
https://qiita.com/yuyasat/items/49e3296f3c64fccc7811
https://developer.mozilla.org/ja/docs/Web/API/History/replaceState

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?