Like button Ajax in Ruby on Rails
You can do so in various ways, the simple way goes like this:
Preparations
Include Rails UJS and jQuery in your
application.js
(if not already done so)://= require jquery
//= require jquery_ujsAdd
remote: true
to yourlink_to
helpers:<%= link_to "Like", '...', class: 'vote', method: :put, remote: true %>
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:
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 thisPlease also note the use of
data-id
, notid
. If this snippet gets used often, I'd refactor it into a helper method.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
#…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
returnAgain, 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:
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
).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
Node.Js App Can't Run on Port 80 Even Though There's No Other Process Blocking the Port
Execjs::Runtimeerror on Windows Trying to Follow Rubytutorial
Find the Longest Common Starting Substring in a Set of Strings
Rails Js.Erb File Cannot Find Method "Render"
How to Determine Equality For Two JavaScript Objects
Get the Values from the "Get" Parameters (JavaScript)
Open a Url in a New Tab (And Not a New Window)
How to Access and Process Nested Objects, Arrays, or Json
How Do JavaScript Closures Work
Dynamically Access Object Property Using Variable
How to Correctly Clone a JavaScript Object
Jquery Ajax Post Example With PHP
Which Equals Operator (== VS ===) Should Be Used in JavaScript Comparisons
Var Functionname = Function() {} VS Function Functionname() {}
Self-References in Object Literals/Initializers