Jason Chu
Jason Chu

Reputation: 385

Rails and AJAX Posting a Comment

I have a simple blog app that reviews video games. Users can post comments on reviews.

My problem is that when I try to post a comment using AJAX(I'm a student trying to teach myself) on a review page, the page does a full refresh. The data is still submitted correctly, but then I have to reload the page in order to show the comments.

review.js

$(document).ready(function() {
  $('#new_comment').on('submit', function(e) {
     url = this.action

     data = {
      'authenticity_token': $("input[name='authenticity_token']").val(),
      'comment': {
        'content': $("#comment_content").val(),
      'user_id': $("#comment_user_id").val()
      }
    };

    $.ajax({
      type: "POST",
      url: url,
      data: data,
      success: function(response) {
        $('#comments-section ol').append(response);
      }
    });

    e.preventDefault();
  });
});

comments_controller.rb #create action

  def create
    @review = Review.find_by(params[:id])
    @comment = @review.comments.new(comment_params)

    if !current_user
      redirect_to review_oath(@review), alert: "You must be logged in to add a comment"
    else
      @comment.save
      redirect_to @review
    end
  end

  private

  def comment_params
    params.require(:comment).permit(:content, :user_id)
  end

review.show.html.erb

        <div id="comments-section">
          <% if @review.comments.any? %>

            <p>
              <div><br>
                <br>
              <ul>
                <% @review.comments.all.each do |c| %>
                  <h4 class="media-heading"><%= c.user.email %> said</h4> <br><li><p><%= c.content %></p></li>
                <% end %>
              </ul>
            </p>
          <% end %>
        </div>
      </div>

The full repo is here, make sure you're on the AJAX/JQUERY branch: https://github.com/jchu4483/Rails-Assessment-/tree/ajax/jquery

Thanks, and any help is much appreciated.

Upvotes: 0

Views: 1464

Answers (1)

Pablo Gonzaga
Pablo Gonzaga

Reputation: 411

First of all, in order to ensure the app security, I suggest you to move the authentication method to a before_action in ApplicationController and skip it for the public actions.

If you are using devise gem you can add:

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception

  before_action :authenticate_user!
end

Using this your app is secure by default.

For the comment update I recommend you to follow this approach:

Make your comment creation form an AJAX form adding the remote param and move it to a partial:

_form.html.erb

<%= form_for comment, remote: true do |form|%>
  <%=form.hidden_field :review_id, review_id%>
  <%=form.text_field :content%>
  <%=form.submit :save%>
<% end %>

Create comments list partial too:

_comments.html.erb

<table>
  <thead>
    <tr>
      <th></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <% comments.each do |comment| %>
      <tr>
        <td>
          <%= image_tag comment.user.avatar.url %>
        </td>
        <td>
          <%= comment.content %>
        </td>
      </tr>
    <% end %>
  </tbody>
</table>

So your review page should be something like this:

reviews/show.html.erb

<div class="review-photo">
  <%= @review.photo %>
</div>
<div class="review-content">
  <%= @review.content %>
</div>
<section class="comments">
  <div class="new-comment-container">
    <%= render 'my_new_comment_form_path', comment: Comment.new, review_id: @review.id %>
  </div>
  <div class="comments-container">
    <%= render 'my_comments_partial_path', comments: @review.comments %>
  </div>
</section>

Update your comments controller in order to response to AJAX on the create action:

   def create
    format.js do
      @review = Review.find_by(params[:review_id])
      @review.comments << Comment.create(comment_params)
    end
  end

  private

  def comment_params
    params
      .require(:comment)
      .permit(:content)
      .merge(user_id: current_user.id)
  end

This code has a little refactor to ensure the commenter is the current user.

Then you should create a create.js.erb file to response to the create action, in this file you should replace the old list and the old form with the new ones:

comments/create.js.erb

$('.new-comment-container').html("<%= j render 'my_new_comment_form_path', comment: Comment.new, review_id: @review.id%>")
$('.comments-container').html("<%= j render 'my_comments_partial_path', comments: @review.comments%>")

I think this is a clean way to work with AJAX forms in rails.

Upvotes: 2

Related Questions