问题
I'm new to Ruby On Rails, I used the acts_as_votable gem to create Like and Unlike Buttons to make Users like and unlike Posts but I can't make them change from Like to Unlike (and viceversa) and update the counter each time they click without refreshing the page. I tried following other kind-of-similar answers but I had no luck. Without the messy changes I tried to do to implement Ajax my code looked like this:
Post Model acts_as_votable and User Model acts_as voter
Posts Controller has
def like
  @post = Post.find(params[:id])
  @post.liked_by current_user
  redirect_to :back
end
def unlike
  @post = Post.find(params[:id])
  @post.unliked_by current_user
  redirect_to :back
end
Routes have
resources :posts do
  member do
    put 'like', to: "posts#like"
    put 'unlike', to: "posts#unlike"
  end
end
View has
<%= @post.get_likes.size%>
  <% if @post.get_likes.size ==1 %>
    person like this
  <% else %>
   people like this
<% end %>
<div class="btn-group">
  <% if (current_user.liked? @post) %>
    <%= link_to unlike_post_path(@post), method: :put, class: "btn btn-default btn-sm" do %>
    <span class="glyphicon glyphicon-chevron-down"></span>
      Unlike
    <%end %>
  <% else %>
    <%= link_to like_post_path(@post), method: :put, class: "btn btn-primary btn-sm" do %>
    <span class="glyphicon glyphicon-chevron-up"></span>
      Like
    <% end %>
  <% end %>
</div>
I read a lot of answers about Ajax but I was unable to replicate the results. Thank you in advance!
回答1:
First, you need to point out your posts controller to respond to js format. Then the two actions in posts_controller become:
def like
  @post = Post.find(params[:id])
  @post.liked_by current_user
  respond_to do |format|
    format.html { redirect_to :back }
    format.js { render layout: false }
  end
end
def unlike
  @post = Post.find(params[:id])
  @post.unliked_by current_user
  respond_to do |format|
    format.html { redirect_to :back }
    format.js { render layout: false }
  end
end
Second, you need to pass remote: true on your links:
 <div class="votes">
    <% if current_user.liked? @post %>
       <%= link_to unlike_post_path(@post), method: :get, remote: true, class: 'unlike_post' %>
     <% else %>
       <%= link_to like_post_path(@post), method: :get, remote: true, class: 'like_post' %>
     <% end %>
  </div>
I change method: :put to method: :get, so change it in your config/routes.rb, and add a class to your links to bind it in js.
Finally, you need to create 2 views in app/views/posts/:
- like.js.erb - $('.like_post').bind('ajax:success', function(){ $(this).parent().parent().find('.vote_count').html('<%= escape_javascript @post.votes_for.size.to_s %>'); $(this).closest('.like_post').hide(); $(this).closest('.votes').html(' <%= link_to "Unlike", unlike_post_path(@post), remote: true, method: :get, class: 'unlike_post' %>'); });
- unlike.js.erb - $('.unlike_post').bind('ajax:success', function(){ $(this).parent().parent().find('.vote_count').html('<%= escape_javascript @post.votes_for.size.to_s %>'); $(this).closest('.unlike_post').hide(); $(this).closest('.votes').html(' <%= link_to "Like", like_post_path(@post), remote: true, method: :get, class: 'like_post' %>'); });
To handle update of count, I use a .vote_count class, so in your view:
<div class="vote_count">
  <%= @post.get_likes.size %>
</div> 
So my view:
<div>
  <div class="vote_count">
    <%= @post.get_likes.size %>
  </div> 
  <div class="votes">
    <% if current_user.liked? @post %>
      <%= link_to unlike_post_path(@post), method: :get, remote: true, class: 'unlike_post' %>
    <% else %>
      <%= link_to like_post_path(@post), method: :get, remote: true, class: 'like_post' %>
    <% end %>
  </div>
</div>
Edit: I edit my answer. Update your links with class instead id. And take a look at 2 js view to find closest(). It works well in index and show page in my sandbox app. So feel free to adapt to your markup.
来源:https://stackoverflow.com/questions/31251246/rails-acts-as-votable-gem-like-unlike-buttons-with-ajax