
Providing a seamless user experience often means removing the need to refresh a page when adding or removing content. This can be done by working with AJAX within your Rails application.
In the traditional Rails blog example where you have users, posts and comments, you may want to use :remote => true to add comments to posts without having to refresh the page. This can be accomplished in a few simple steps.
1. Add :remote => true to Your Form
Add :remote => true to your form, which should be included in a partial in views/comments and rendered in the show.html.erb page for views/posts:
<%= form_for [@post, Comment.new], :remote => true do |f| %> <div class="field"> <%= f.text_area :content, class: "form-control col-lg-8", placeholder: "Provide comment here." %> <%= f.hidden_field :post_id, :value => @post.id %> <%= f.hidden_field :user_id, :value => current_user.id %> </div> <div class="actions"> <%= f.submit "Leave Comment" %> </div> <% end %>
You can render this form in your show page by including the following:
<%= render 'comments/new', review: Comment.new(post_id: @post.id) %>
Adding :remote => true to your form submits the data to your database without refreshing the page. However, this information will not automatically be rendered by the browser. Until you add the appropriate JavaScript to append this information to the DOM, you’ll have to refresh the page to see the information displayed (which defeats the purpose of using AJAX in the first place).
2. Update Your Controller
In your comments_controller.rb, update your create function to include format.js {}:
# POST /comments # POST /comments.json def create @comment = Comment.new(comment_params) respond_to do |format| if @comment.save format.html { redirect_to @comment.post, notice: 'Comment was successfully created.' } format.js { } format.json { render :show, status: :created, location: @comment } else format.html { render :new } format.json { render json: @comment.errors, status: :unprocessable_entity } end end end
This allows the create function to respond with the appropriate JavaScript.
3. Add create.js.erb File
Add a create.js.erb file in your views/comments directory:
# confirm file called console.log("create.js.erb file"); # add new comment to end of comments section $("#comments").append("<%= @comment.user.name %>: <%= @comment.content %>"); # remove current DOM element stating there are no comments yet (if no comments yet exist) $("#comments h4").html(""); # replace comment form so user can only comment once (if desirable) $(".comment_form").html("<h1>You have commented on this post</h1>");
You may see a 500 Internal Server Error, or your JavaScript file may not initially work. This is a common problem and usually due to a missing partial. You should review your logs to determine what is missing and what additional partials you may need to create.
- PM Career Story - April 28, 2022
- How to Transition into Product Management - December 26, 2017
- What I’ve Learned in My First Few Months as a Product Manager - October 14, 2015
Pretty sure you don’t need:
@post.id %>
in your form as your using a nested form route.
The data does not persist in the app I’m working on if that is removed.
Me and this article, sitting in a tree, L-AE–R-N-I-N-G!
Awesome.
I’m not sure, but isn’t it possible with those hidden_fields to just change value of users id in html and write comments as other user?
I use a similar approach and had that concern. A simple solution: manually verify in the controller that current_user == @comment.user. If it’s false, I just redirect to root_url (not going to spend too much energy explaining the error in this scenario). There may be some way to do a validation in the model as well, but my controller approach seemed straightforward and reliable enough.
You’re missing Step 4: How to write a test to verify this is working :p
carpe diem