ポートフォリオとして投稿サイトを作成しています。コメント機能実装時に、部分テンプレートで呼び出した部分でMissingTemplateのエラーが生じたので、その解決策について書いていこうと思います。
実装したい機能
記事詳細ページでコメント投稿機能実装
エラー詳細
コメントフォーム内にコメントを記述して送信ボタンを押した場合は正しく保存されるが、コメントフォームが空欄のまま送信ボタンを押すと、下記のように部分テンプレート部分でMissingTemplateエラーが発生。
コード
エラー発生時のビュー・コントローラーに関するコードは、以下の通り。
ビュー
部分テンプレート切り出した_article.html.erbを呼び出すことで記事の内容を表示し、その下にコメントフォームを実装。
<div class="show-main">
<div class="show-post">
<%= render partial: "article", locals: {article: @article}%>
</div>
<div class="comment-form">
<%= form_with model: [@article, @comment], url: article_comments_path, local: true do |f| %>
<%= render 'shared/error_messages', model: f.object %>
<div class="comment-main">
<%= f.text_area :message, placeholder: "コメントする", class: "comment-text" %>
<%= f.submit '送信', class: "comment-btn" %>
</div>
<% end %>
</div>
</div>
コントローラー
commentsコントローラーおよびarticlesコントローラーは以下の通り。
articles#showにてコメントフォームを表示し、comments#createにてコメントを保存。また、comments#createでは、コメントの保存が成功した時と失敗した時で条件分岐を記入。
class CommentsController < ApplicationController
def create
@article = Article.find(params[:article_id])
@comment = @article.comments.new(comment_params)
@comments = @article.comments.includes(:user)
if @comment.save
redirect_to article_path(@article)
else
render template: "articles/show"
end
end
private
def comment_params
params.require(:comment).permit(:message).merge(user_id: current_user.id, article_id: params[:article_id] )
end
end
class ArticlesController < ApplicationController
#中略
def show
@article = Article.find(params[:id])
@comment = Comment.new
@comments = @article.comments.includes(:user)
end
end
エラーの原因とその解決策
コメントが空欄の時のみエラーが生じることから、レンダリングの部分を見直し。その結果、部分テンプレートのURLを"article"→"articles/article"に変更することで、エラーが解決。これは、_article.html.erbファイルを呼び出す際、以下のような関係による。
・articles#showのビューからの部分テンプレート呼び出し
→articlesフォルダ内からの呼び出しになるためURLの指定は"article"で十分
・commentsコントローラーからarticles#showへアクセスする時の部分テンプレート呼び出し
(コメント投稿が失敗時)
→commentsフォルダ内にいるため、より一層上の"articles/article"のURLの指定が必要
また、コメントが正しく投稿された場合にエラーが起きなかったのは、コメント投稿成功時のパスの取得はredirect_toを用いており、ルーティングを通してページ遷移が行われたためである。
完成コード
show.html.erbの部分テンプレート部分のURLを"article"→"articles/article"に変更
<div class="show-main">
<div class="show-post">
<%= render partial: "articles/article", locals: {article: @article}%>
</div>
<div class="comment-form">
<%= form_with model: [@article, @comment], url: article_comments_path, local: true do |f| %>
<%= render 'shared/error_messages', model: f.object %>
<div class="comment-main">
<%= f.text_area :message, placeholder: "コメントする", class: "comment-text" %>
<%= f.submit '送信', class: "comment-btn" %>
</div>
<% end %>
</div>
</div>
まとめ
commentsコントローラーでは、レンダリングのパスはちゃんと一階層上の"articles/show"を指定しているのになんで正しく実装出来ないんだろうと思い、このエラーにかなり苦戦しました。結果的には、レンダリング時には、コントローラー→ビューに直接リクエストが送られるという部分の理解が不足していたのが一番の原因かなと思います。僕と同じく、初学者の方がコメント機能を実装する時に、この記事が何か助けになれば幸いです。
参考
・redirect_toとrenderの違いについて
https://qiita.com/morikuma709/items/e9146465df2d8a094d78