Taylor
Taylor

Reputation: 177

Multiple AJAX forms in the same view in Rails

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

Answers (2)

Yan Foto
Yan Foto

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

Finks
Finks

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

Related Questions