Reputation: 177
I have a view of listings and each listing has a 'favorite' button associated with it. This button submits an AJAX form, either liking or unliking the listing if it has already been liked. Submitting the form calls a like.js.erb method or destroy.js.erb which changes the state of the button.
However, since the js.erb files target the #id of the element I am changing, this only works for the first element. I also can't use a class because that changes all elements.
What's the best way to go about allowing users to like and unlike listings, and changing the view without reloading the page?
I tried giving each of the IDs a unique number based on the listing ID, but can't pass the listing ID to the js.erb files so that doesn't work.
Here is some relevant code:
/listings/_like.html.erb
<%= link_to like_listing_path(@listing), method: :put, :remote => true, :class => "like-btn" do %>
<i class="fa fa-heart-o"></i>
<% end %>
/listings/like.js.erb
$("#like-form").html("<%= escape_javascript(render('listings/unlike')) %>")
$("#like-size").html('<%= @listing.get_likes.size %>')
/listings/unlike.html.erb
<%= link_to unlike_listing_path(@listing), method: :put, :remote => true, :class => "unlike-btn" do %>
<i class="fa fa-heart liked"></i>
<% end %>
/listings/unlike.js.erb
$("#like-form").html("<%= escape_javascript(render('listings/like')) %>")
$("#like-size").html('<%= @listing.get_likes.size %>')
Thank you!
Upvotes: 0
Views: 827
Reputation: 11378
The Rails Way
Just append the ID of the listing to their ID in your erb (since your form is not there I am assuming some structure as the following):
<div id="like-form-<%= @listing.id %>">
<!-- your content -->
</div>
and do the same in your JS file:
$("#like-form-<%= @listing.id %>").html("<%= escape_javascript(render('listings/unlike')) %>")
$("#like-size-<%= @listing.id %>").html('<%= @listing.get_likes.size %>')
This way you will always target the correct element.
JS way
Let's say that you have a list of listings
generated through a loop and each item has the following markup:
<div class="listing">
<!-- your content -->
<div class="like-form">
<%= link_to like_listing_path(@listing), class: "like-btn" do %>
<i class="fa fa-heart-o"></i>
<% end %>
<%= link_to unlike_listing_path(@listing), class: "unlike-btn" do %>
<i class="fa fa-heart liked"></i>
<% end %>
</div>
</div>
Now you could do something write some JS like this:
$(".like-btn, .unlike-btn").click(function(e) {
e.preventDefault();
var el = $(this);
$.ajax({
url: el.attr("href"),
method: post
}).done(function(data) {
el.parent().html(data);
});
});
Same thing applies for the number of likes.
A word on REST
I have notices that you have two different paths to like and unlike so I am assuming that you have added those paths manually to your routes.rb
. If you really want to leverage the power of REST you really only need a single path to your listing like and then use POST
to create a like and DELETE
to remove a like (i.e. unlike).
Upvotes: 3
Reputation: 1681
How about using data attributes?:
<%= link_to like_listing_path(@listing), method: :put, :remote => true, :class => "like-btn", :"data-id" => user.id do %>
<i class="fa fa-heart-o"></i>
<% end %>
Then on your like
action
def like
@listing = Listing.find params[:"data-id"]
@listing.like!
end
Upvotes: 0