pyfl88
pyfl88

Reputation: 1710

Rails ajax on follow button

I am creating a page with a list of books and follow button on each book.

shared/_books.html.erb

<% @books.each do |book| %>
 <%= book.name %>
 <% if book.followed_by(current_user) %>
  <%= link_to 'Unfollow', unfollow_book_path(book), remote: true %>
 <% else %>
  <%= link_to 'Follow', follow_book_path(book), remote: true %>
 <% end %>
<% end %>

controllers/books_controller.rb

def index
 @books = Book.all
 respond_to do |format|
   format.html
   format.js
 end
end

In my follow.js.erb, how can I change the button to unfollow button when I click on the follow button? Currently I am using a method to render the list of books like this:

books/follow.js.erb

$('#books').html("<%= escape_javascript(render partial: 'shared/books' , collection: @books, as: "book") %>");

This is working but it is rendering the whole list of books again, which I don't want, any ideas how can I just refresh the follow button? Thanks

Upvotes: 0

Views: 253

Answers (2)

Richard
Richard

Reputation: 11

This is a perfect example of where React is useful, since it can be set up to re-render only part of a page.

If you're looking to do this without adding additional tech to the stack, you could set up a function that intercepts the click event and sends an AJAX call instead of using a rails helper link, then have the controller send back a JSON (or some other object you can identify) to indicate the current status and modify just the clicked link.

A purely manual approach might look something like this, but there are a number of ways to optimize and simplify this code. Here, this is simply intended as a drawn out view to illustrate what's happening..

shared/_books.html.erb

<% if book.followed_by(current_user) %>
  <a class = 'unfollow' href="#" onClick='followClicked(this)'>Unfollow</a>
<% else %>
  <a class = 'follow' href="#" onClick='followClicked(this)'>Follow</a>
<% end %>

books/follow.js.erb

function followClicked(event){
  var ajax_url = event.target.hasClass('unfollow') ? <%=unfollow_book_path(book)%> : <%=follow_book_path(book)%>
  $.ajax({
    url: ajax_url
    type: 'GET'
  }).success(function(data){
    if(data["new_class"] == 'unfollow'){
      event.target.removeClass('follow');
      event.target.addClass('unfollow');
      event.target.text("Unfollow");
    } else {
      event.target.removeClass('unfollow');
      event.target.addClass('follow');
      event.target.text("Follow");
    }
  });
}

In the above, the controller is expected to return a JSON that looks like:

{'new_class' => 'follow'}

Again, this is only one approach and there are many others (some more easy to understand that others ;) ). I'm sure someone here probably has other ideas that will work just as well, or better.

Upvotes: 1

Ty Shaikh
Ty Shaikh

Reputation: 181

One way to do this would be to create a div around the if/else block in your partial. Give this div an id that will be based on the book.id and therefore unique.

In your follow.js.erb, reuse the logic from you partial.

<% if @book.followed_by(current_user) %>
  $("#book-<%= @book.id %>").html("<%= link_to 'Unfollow', unfollow_book_path(@book), remote: true %>")
 <% else %>
  $("#book-<%= @book.id %>").html("<%= link_to 'Follow', follow_book_path(@book), remote: true %>")
 <% end %>

You may have to change your routes / controllers. I am not sure how your unfollow_book_path and follow_book_path work.

Upvotes: 1

Related Questions