kouの技術的メモ

学習した内容の定着やアウトプット用に開設しました

成果物の作成11 投稿記事に対するコメント機能のAjax化

せっかくなので、投稿に対するコメント機能をAjax化してみました。

まずコントローラ側を作ります。

今までは単にfallback_location: root_pathにより、リダイレクトで再描画していたのですが、

respond_toでAjaxを扱う部分を実装します。

その際、JavaScriptが使える場合と、

もしブラウザ側でJavaScriptが無効の設定になっていた場合と書き分けます。

app/controllers/comments_controller.rb

  def create                                                                   
    @comment = Post.find(params[:post_id]).comments.new(comment_params)         
    @comment.user_id = current_user.id                                          
    if @comment.save!                                                           
      respond_to do |format|                                                   #ここをredirect_back(fallback_location: root_pathから、respond_toに
        format.html { redirect_back(fallback_location: root_path) }
        format.js   { render :comment_view}
      end
    else                                                                        
     render 'posts/show'                                                      
    end
  end

そして format.js { render :comment_view}で呼び出すcomment_view.js.erbを作ります

$("#comment_view").html("<%= j(render'comments/comment_view', { comments: @comment.post.comments }) %>");

上で呼び出され、Ajaxにより描画される部分をパーシャル化します

views/comments/_comment_view.html.erb

<h2>コメント一覧</h2>
<% comments.each do |c| %>
    <%= link_to c.user.name, user_path(c.user_id) %>
    <%= c.content %>
    <hr>
<% end %>

こちらがビュー側のパーシャル呼び出しです

<div id="comment_view">
<%= render partial: 'comments/comment_view', locals: { comments: @comments } %>

ここがポイントなのですが、パーシャルを呼び出すビュー側でインスタンス変数@commentsは直接渡さず、ローカル変数commentsに変換して渡しています。

何故かと言うと、最初にアクションから渡されるインスタンス変数@commentsはposts_controllerのshowアクションで渡される@commentsで、

コメントを投稿した後に渡されるのは、comment_contorollerのcreateアクションの@commentsでそれぞれ違うものだからです。

たまたま同じインスタンス変数名だったからいいものの、そうでない場合も多いと思われますし、保守性を高める意味でも

パーシャル_comment_view.html.erbには、一度ローカル変数に変換して渡しています。

ここらへんの取扱に悩んで結構時間使いましたね。

今回、色々検索しまくりました。

そして最後にform_forにremote:trueを追加して、config/application.rbで認証トークンを埋め込み完成です! フォーム

 <%= form_for([@post, @comment], remote: true) do |f| %>
    <%= f.text_field :content %>
    <br>
    <%= f.submit 'コメントする' %>
    <% end %>
<% end %>

config/application.rb

   config.action_view.embed_authenticity_token_in_remote_forms = true

自動テストと、JavaScriptをブラウザで切っても動作するか、手動でコメントを投稿するテストも行い、最後にgitとherokuにpush。

また、テストの結果

CommentモデルとPostモデルの整合性が取れておらずコメントがある記事を削除しようとするとエラーが出る重大バグがあったので、

dependent: destroyを追加し、修正しておきました。

/models/post.rb

has_many :comments,dependent: :destroy         

リダイレクトされないでリアルタイムに反映されるので、なんかスタイリッシュになりましたね!

少しずつ高度なこと学習し、アウトプットできるようになるとすごく楽しいです!