Reputation: 1710
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
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
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