Like Button Ajax in Ruby on Rails

Like button Ajax in Ruby on Rails

You can do so in various ways, the simple way goes like this:

Preparations


  1. Include Rails UJS and jQuery in your application.js (if not already done so):

    //= require jquery
    //= require jquery_ujs
  2. Add remote: true to your link_to helpers:

    <%= link_to "Like", '...', class: 'vote', method: :put, remote: true %>
  3. Let the controller answer with a non-redirect on AJAX requests:

    def like
    @content = Content.find(params[:id])
    @content.liked_by current_user

    if request.xhr?
    head :ok
    else
    redirect_to @content
    end
    end

Advanced behaviour

You probably want to update the "n users liked this" display. To archieve that, follow these steps:

  1. Add a handle to your counter value, so you can find it later with JS:

    <span class="votes-count" data-id="<%= @content.id %>">
    <%= @content.get_likes.size %>
    </span>
    users like this

    Please also note the use of data-id, not id. If this snippet gets used often, I'd refactor it into a helper method.

  2. Let the controller answer with the count and not simply an "OK" (plus add some information to find the counter on the page; the keys are arbitrary):

    #…
    if request.xhr?
    render json: { count: @content.get_likes.size, id: params[:id] }
    else
    #…
  3. Build a JS (I'd prefer CoffeeScript) to react on the AJAX request:

    # Rails creates this event, when the link_to(remote: true)
    # successfully executes
    $(document).on 'ajax:success', 'a.vote', (status,data,xhr)->
    # the `data` parameter is the decoded JSON object
    $(".votes-count[data-id=#{data.id}]").text data.count
    return

    Again, we're using the data-id attribute, to update only the affected counters.

Toggle between states

To dynamically change the link from "like" to "dislike" and vice versa, you need these modifications:

  1. Modify your view:

    <% if current_user.liked? @content %>
    <%= link_to "Dislike", dislike_content_path(@content), class: 'vote', method: :put, remote: true, data: { toggle_text: 'Like', toggle_href: like_content_path(@content), id: @content.id } %>
    <% else %>
    <%= link_to "Like", like_content_path(@content), class: 'vote', method: :put, remote: true, data: { toggle_text: 'Dislike', toggle_href: dislike_content_path(@content), id: @content.id } %>
    <% end %>

    Again: This should go into a helper method (e.g. vote_link current_user, @content).

  2. And your CoffeeScript:

    $(document).on 'ajax:success', 'a.vote', (status,data,xhr)->
    # update counter
    $(".votes-count[data-id=#{data.id}]").text data.count

    # toggle links
    $("a.vote[data-id=#{data.id}]").each ->
    $a = $(this)
    href = $a.attr 'href'
    text = $a.text()
    $a.text($a.data('toggle-text')).attr 'href', $a.data('toggle-href')
    $a.data('toggle-text', text).data 'toggle-href', href
    return

    return

Rails/JS - Like button with AJAX in 2018

controllers/likes_controller.rb

  def create
@beat = Beat.find(params[:beat_id])

if already_liked?
flash.now[:notice] = "You can't like more than once!"
else
@beat.likes.create(user_id: current_user.id)
end
end

views/beats/show.html.erb

<p id="likes-count"><%= pluralize(@beat.likes.count, 'Like') %></p>
<div id="like-button"><%= like_button_tag @beat, previous_like %></div>

helpers/beats_helper.rb

module ApplicationHelper
def like_button_tag beat, previous_like
return button_to 'Unlike', beat_like_path(beat, previous_like), method: :delete, remote: true if previous_like
button_to 'Like', beat_likes_path(beat), method: :post, remote: true
end
end

views/beats/create.js.erb

$('#likes-count').html('<%= pluralize(@beat.likes.count, 'Like') %>');
$('#like-button').html('<%= like_button_tag @beat, previous_like %>');

you can also use haml instead of erb

Adding ajax to like button in Rails app

Here I am using a single method as you never want the likes count to go below zero.

Whenever the user likes a post, the count increments by 1 and the color of the thumbs-up icon changes(just to show a visual change).

When the user clicks on an already liked post the count of likes should be reduced by one so we should use the unliked_by method provided by the gem.

For implementing this we can use an upvote.js.erb file (remember the file name should be same as your controller method name it makes the process easier).

Then in the .js.erb file we can mix ruby with js and have the desired result as shown in the upvote.js.erb file.

I add an extra class called liked which changes the color of the button.

Please Note: I am assuming that you have a single post on a page. If you have multiple posts on a page then you should add ID's to the posts container and similarly change the js code in the upvote.js.erb file.

posts_controller.rb

  def upvote
if !current_user.liked? @post
@post.liked_by current_user
elsif current_user.liked? @post
# as the above method can also result nil if he is yet to vote
@post.unliked_by current_user
end
end

views/posts/upvote.js.erb

<% if current_user.liked? @post %>
document.getElementsByClassName('like-btn')[0].className = "like-btn liked";
<% else %>
document.getElementsByClassName('like-btn')[0].className = "like-btn";
<% end %>
document.getElementsByClassName('likes-count')[0].innerHTML="<%= @post.get_upvotes.size %>";

view.html.erb

<!-- Adding a class liked -->
<style>
.liked {
color: blue;
}
</style>
<%= link_to like_post_path(@post), class:"like-btn", method: :put, remote: true do %>
<button class="btn btn-warning">
<span><p><i class="fa fa-thumbs-up fa-lg" aria-hidden="true"></i></p></span>
</button>
<% end %>
<span class="likes-count"> <%= @post.get_upvotes.size %> </span>

Simple like/unlike button with rails 3, jquery, and ajax

My answer to your question is long, so I wrote up an example application. Here's a snippet:

There are many ways to skin this cat, but I like to render a partial and a single ujs template.

_like_button.html.erb:

<% if like = current_user.likes.find_by_product_id(@product.id) %>
<%= form_for like, :html => { :method => :delete },
:remote => true do |f| %>
<%= f.submit "Unlike" %>
<% end %>
<% else %>
<%= form_for current_user.likes.build(:product_id => @product.id), :remote => true do |f| %>
<%= f.hidden_field :product_id %>
<%= f.hidden_field :user_id %>
<%= f.submit "Like" %>
<% end %>
<% end %>

toggle.js.erb, where "#like" is the div enclosing the form:

$("#like").html("<%= escape_javascript render('like_button') %>");

Ruby on Rails Like Button with Image no text

I found this awesome repository on GitHub that helped me out. Special thanks to the author.

https://github.com/shihabullal/soquestion6482354

Rails: Ajax for Like Button and Updating Like Count

But why are you doing this in your view?

<% @post = user.posts.first %>

It will update only your first post always. I can't understand.

Try with this:

_liked_users.html.erb

<%= @updated_post = Post.where(id: @post.id) %>    
<%= "#{@updated_post.likes.length} like this" %>

or

<%= @post.reload %>    
<%= "#{@post.likes.length} like this" %>

Edit (Try to build your like this):

def create
@post = Post.where(id: params[:post_to_be_liked])
@like = @post.likes.build(:post_id => @post.id)
if @like.save
respond_to do |format|
format.html {redirect_to :back, notice: "Liked!"}
format.js
end
end
end

Adding AJAX to Rails 4 Like/Unlike Button with Counter

You are redirecting the request before reaching the respond_to block. Anything that comes after redirect_to will not run. You are also missing an end statement to close your respond_to block. I'm assuming you only want to redirect the user on an HTML request. If so change the ending of your create and destroy methods to this:

respond_to do |format|
format.html { redirect_to project_path(project), :notice => "Some message" }
format.js { render 'your_javascript_file' }
end

Now you'll need some Javascript files to actually change the web page. Whether you'll need one or two Javascript files is up to you and how you want each link to behave after it's clicked.



Related Topics



Leave a reply



Submit