2
\$\begingroup\$

I have the following method which creates a like for a post:

def create
  post.likes.create(user: current_user)
  respond_to do |format|
    format.html { redirect_to post_redirect(post) }
    format.js
  end
end

We don't really care whether the create was successful or not and either redirect the user to the post (the page where the method is called) or return some js if done via AJAX (usually).

The js partial looks like:

// create.js.erb
$("#content").html("<%= j render(:partial => 'likes/like_button') %>");

However I didn't like that it expects the AJAX request to request JS and then having a partial render inside a jQuery method that then makes an assumption about #content existing to render it inside. So effectively the response is also handling how it should be used.

My alternative approach was:

def create
  post.likes.create(user: current_user)
  request.xhr? ? (render :partial => 'likes/like_button') : (redirect_to post_redirect(post))
end

This way we just return the partial or redirect depending on if the request was via AJAX.

The former seems to be the standard Rails approach (according to most docs) but I find the latter far cleaner as it means I can handle how the response is handled as it just returns HTML rather than having the actual handling of the response being returned with the response.

Is there a better way to handle AJAX responses? Or reasons why my approach would be considered bad practice? Of course using the first approach means using Rails UJS and my own uses custom JS to make the request and handle the response.

\$\endgroup\$
1
\$\begingroup\$

However I didn't like that it expects the AJAX request to request JS and then having a partial render inside a jQuery method that then makes an assumption about #content existing to render it inside. So effectively the response is also handling how it should be used.

I actually think this is best practice for a non-SPA solution. It's a great approach because you will know how the page is structured. It works even if there's multiple posts because you can assign object IDs to the posts in the HTML, which then lets you target those objects in your JQuery selector.

So you'd be looking at something like:

$('.post[data-object-id=<%= post.id %> .likes').html('<%= j render(partial: 'likes/like_button', locals: {likes: post.likes}) %> ')

Now if you want to do something fancy with the HTML beyond just inserting it at the appropriate location, then you'd indeed make an AJAX request for the HTML, and attach a handler to the promise generated from the AJAX request which would handle the HTML response appropriately.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.