Skip to main content

Liking Posts Discussion

General • Asked by Chris Oliver
Related episode

Learned a lot of new things through this post, thank you. Never knew how to properly write a route to a module or the proper architecture for that set up. Very useful stuff. Would have liked to see you use div_for and dom_id methods when it comes to assigning the ids to the p tags and rendering out JS at the end. I feel like both of those are under utilized functions that handle a lot of the hard coded id values, etc...

It's like you know what's going through my mind. I'm making a dedicated screencast for div_for. ;-)

RIght on! I'm excited to hear your insight on it. It's a practice I only started using a few weeks ago, and you seem to have a way of explaining things I already know in a way that helps me understand them on a deeper level. Thank you again :)


Great screencast! I'm behind on my rails development because of a really good Ruby course I'm in, but I will try this out soon. Thank you so much for these!


Hey! I really appreciate all these screencasts, happily pro user. For this I'm getting an "undefined method 'likes'" on the user model so i'm a little stuck. Any ideas why?

Cheers! :)

Well, i restarted the server several times and somehow it worked :)

Haha! It's possible there was something accidentally getting cached, but it's hard to say.

:P Wouldn't be the first time magic happens on server restart. One thing that didn't work for me at the end is redirection to login page when a user is not logged in. I'm getting the "POST /unauthenticated HTTP/1.1" 401 in the server, but no actual redirection. Any idea why? (do you have a repo for this course?)

Cheers from Ecuador!


Thank you for these screencasts! They are awesome. I keep getting an error "Routing Error uninitialized constant Posts" when I click the 'like' link. I feel like this is a fairly simple fix? Any help would be much appreciated. Thanks in advance.

If it's in reference to your Post database model (That's my guess) then you want to reference singular "Post" and not plural "Posts" in your controller. Pluralization can get you easily with Rails. :)

Thank you for responding! I got past the uninitialized constant error now I keep getting 'undefined method likes'. I am trying to set up this like button on a nested resource (a lesson inside of a course), would that change things? Thanks again

Not really. The only thing that will change is querying for the nested object and adding likes to it.

Your controller would do something like:

@course = Course.find(params[:course_id])
@lesson = @course.lessons.find(params[:id])

And then as long as you have your likes association on the Lesson model, you should be good to go.

I didn't have has_many: likes in my Lesson model! Thank you! I also had to add @course = Course.find(params[:course_id]) in my def create and def create part of my Lesson::LikesController for proper routing. It works beautifully. Thanks again for these.

Glad you got it working! :)

It works great. Random question, but is there a way to sort the posts on the index page by most liked?

Sure can. You could use the counter_cache option to save the count to the model and then you can order by that method.

Excellent. Thank you


Chris, very useful screencast.

I can get it to work well with just rails, but when I implement ajax I get the following error.

"ActionView::Template::Error (undefined method `likes' for nil:NilClass):"

I've customized it a bit by making polymorphic 'likeable' likes.

It sounds like your variable that you called ".likes" on was a nil. May need to double check that the find is grabbing the post (or whatever model) is returning the right object.


Thanks for that screencast. I even watched the forum series and the "polymorphic comments". I m wondering what we do if we want to have likes in our forum for threads and posts? Would you use the polymorphic associations?

Yep! You can make the likes polymorphic if you want them to apply to more than one model. That would let you add likes to Threads and Posts. You'd build it pretty much the same way and that should do the trick.


Chris,

Any idea what query I could put in my user model to find out all posts that have not (yet) been liked by the user?

The easiest way is to use a counter cache column. It can keep track of how many likes each post has and saves it on the post so that you can quickly and easily query that. There's an old Railscast on it that I'd recommend: http://railscasts.com/episo...

You could also do it with a join and a group by query, but a counter cache will let you sort the information at any time quickly.

Awesome, many thanks ;)


This is awesome! Just the functionality I need. Was looking at the "Act-as-votable-gem" but that seems to be overkill as I only need upvotes. Just need to figure out how the gem does the counter caching stuff now

https://github.com/ryanto/a...


Hey Chris,

How do I make the before_action :authenticate_user! to work with ajax? If works perfectly, when there is no remote: true and redirects to the login page if a user is not signed in. However, when using ajax, nothing happens other than a "401 unauthorized" in the logs.

Thanks in advance.

Yeah, that's an issue with redirects and AJAX. jquery_ujs tries to combat that by following the redirect, but it obviously doesn't help since you need to handle that response separately. The simplest solution and what you should generally do is to just disable those links when users are logged out.

If you want to keep them enabled, but then show a sign in form, it's quite a bit more complex and would require you to write your own handler in JS to capture that response and handle it separately.

Thank Chris. Yea I think I'll have to go with the second option. Still trying to master working if JS in rails though.


Hey Chris,

First of all, thank's for the quality of your video.

Unfortunately I'am stuck in the middle of the video, i'am facing with this error : undefined method `likes' for, it highlights

<% if user_signed_in? && current_user.likes?(@pin) %>

@pin is the name for post in my app.
def likes? is written in the User model, could you explain why ?

Cheers

According to the error, it sounds like maybe you don't have "has_many :likes" set up in your User model correctly so it can't find it. Double check that you've got that in there.

And "likes?" is written in the User model as a cleaner syntax for determining if the user likes a an object. Basically you wouldn't want to have the logic for determining that duplicated in your app everywhere, so we put it in a method in the model to make it more readable everywhere else.

Thk's for this very reactive answer, but i double checked on User Model, and i'v got the mention has_many :likes, if you have a another clue, i'am in, is that line correct : <% if user_signed_in? && current_user.likes?(@pin) %>
and this in my User Model :
has_many :likes
def likes?(pin)
pin.likes.where(user_id: id).any?
end

Whoops, actually that is going to call ".likes" on the "pin" variable, so you will need to double check "has_many :likes" on the Pin model. If that is there, then you potentially passed in a nil for pin. Then you'll need to check your controller to make sure that @pin got set to a Pin.


@excid3:disqus I have a likes partial inside of a parent directory like this: app/views/recipes/likes/_like.html.erb. I am trying to render inside the show action of the recipes controller and I am getting an error: missing partial: app/views/recipes/likes/_like. I'm not sure what is throwing the error.
likes partial below:

<% if @recipe.liked_by?(current_user) %>
<%= link_to "Unlike", recipe_like_path(@recipe), method: :post %>
<% else %>
<%= link_to "Like", recipe_like_path(@recipe), method: :post %>
<% end %>

In my Recipes::LikesController create action:

@like = Like.where(user_id: current_user.id, recipe_id: @recipe.id)
if @like.nil?
@like = @recipe.likes.where(user_id: current_user.id).first_or_create!
@recipe_was_liked = true
else
@like.destroy
@post_was_liked = false
end

whoops. I figured it out!


Been looking for a way to do this for a while, great episode...now how would I list all the all the posts that a user has liked? For example, I can do something like this:
<ul>
<%= current_user.owns(@book).each do |book| %>
<li><%= book.id.to_s %></li>
<% end %>
</ul>

...but alas this only shows me the own.id whereas I would like it to show me, for example the book.title. Also, this works with the show action, but in my index it doesn't as I have set up a <% @book.each do |title| %>. I can place the code inline, replacing @book with title but then my ajax button change is rendered useless. I'm fairly new to Rails so any ideas would be welcome!

You can set up an association to get the liked books through the likes the user has. It follows basically the same format. Remember that your book variable from the each is actually a full Book record so you can print out the id, the title, or whatever just by changing what you reference. Here's an example that would list out the book titles that a user likes. (I haven't tested this so it may not work right off the bat)

class User
has_many :likes
has_many :liked_books, through: :likes, class_name: "Book"
end

<%= current_user.liked_books.each do |book| %>
<li><%= book.title %></li>
<% end %>

Just what I needed, thanks Chris! I did go through the has_many documentation for rails and had to change class_name: "Book" to to source: :books though to avoid the to_sym error I was getting. Now I just need to figure out how to make the buttons load via ajax, that only works in the show action. Keep up the awesome work!

A follow-up question...the "like" button works in my show action as that does use @book, but when going through the for each statement above, it will not work as the variable is no longer @book but book. Is there a workaround to this?

So when you loop through each book, you need to reference the "book" local variable, not the "@book" instance variable when rendering the button. This is because the each yields the current book in the loop to the code inside the each block so it can run multiple times every book in the array.

That's what I did but it works when the code is inline in the view itself not when referenced as a partial.

You'll need to make sure you pass the book into the partial because they have different scopes for local variables.


getting an ActionController::RoutingError (uninitialized constant Pins) error in the logs. Can seem to figure it out

Routes are nested like so:

resources :pins do
resource :like, only: [:new,:create, :destroy], module: :pins
end

In Index View- this is pins/likes/like partial

<% if current_user.likes?(pin) %>
<%= link_to "", pin_like_path(pin), method: :delete, class: "fa fa-pinterest-p like", remote: true %>
<% else %>
<%= link_to "", pin_like_path(pin), method: :post, class: "fa fa-pinterest-p ", remote: true %>
<% end %>

Then I have this nested controller with create and destroy actions
class Pins::LikesController < ApplicationController
...
end

fixed it renamed folder from pins_controller to pins


My ajax isn't firing. I am getting an ActionView Template error, undefined local variable or method 'pin'

After I refresh the glyphicon changes color signifying it hit the db. In my index I have <%= render partial: 'pins/likes/like', locals: {pin: pin} %> and the guts of the partial is in my comment below. The partial sits in the block:
@pins.each do |pin|
...
end

Any ideas off the top of your head?

Should it be locals: {pin: @pin} instead? I'm assuming you are just missing sending the pin from the controller.

I was eating dinner and this popped into my head exactly! But thanks so much! You're really great Chris at responding to people's question's. Aside from the great content you release, your willingness to help will keep me coming back to GoRails for a long time - or as long as you keep it live!

Thanks! I appreciate that a lot! :)


I've been playing with joins but can't seem to find the answer: I can displayed a user's liked posts (current.user.liked_posts.each do) but they are sorted by the creation date of the post. How can I sort by the like.created_at date?

You can do a SQL ORDER on the created_cat column by adding liked_posts.order(created_at: :desc) or :asc if you want ascending.

That's what I'm doing...I have current_user.liked_posts.order(created_at: :desc) and it shows the correct posts liked by the user BUT they are ordered by the created_at date of the posts, not the likes.

Nevermind, problem solved. Instead of doing order(created_at: :desc), i did order("likes.created_at desc") and now they are sorted by the like date instead of the post date. Thanks!




Interested to see the source code!


@excid3:disqus Thanks for implementing with basic functionality. I have few questions:

1. On index pages to show all posts, it will have N+1 to check if the current_user like each post. Is there any good way to remove that?
2. I know we could eager load with `includes(:likes)` to solve N+1, just imagine if we have many likes for each post, and we just want to show the total count, and the last 3 likers either radom or from last, how do we do eager load them and prevent to load entire likes collection from db?

The best way is really to load up all the user's likes for those related objects in a single query. Then you can check those post against the likes result with Ruby and that will be 2 queries instead.

You'll have to do some custom SQL to pull off #2, but it's not so bad. Check this out: https://robots.thoughtbot.c...


@excid3:disqus I am not yet a paid member so I couldn't see this screencast yet, I just have one question though. Is this the kind of "LIKE" that the page will not refresh? Because I only knew acts_as_votable now but my problem is if I click the upvote button, the page will refresh.

Yep! It's all AJAX based and includes showing avatars and stuff if you wanted to show a few people's faces who liked the post as well.

Alright! Can't wait to learn this one 😀


Hi, Chris. I am using the friendly_id gem to make the profiles url.
On my like controller I have this def set_profile @profile = Profile.friendly.find(params[:id]) end.
Everything work great but when I click like button I get 404 erro message to console.
The link looks like this
Like and the console error is
POST jquery.self-a714331225d...... http://localhost:3000/profiles/vjandrei/like 404 (Not Found) What I miss here?

Ou I found the problem out :)

def set_profile
@profile = Profile.friendly.find(params[:profile_id])

Glad you got it fixed! 👍

But how about the images. I have in the profiles db image for each profile and try to get the image now like this.
<% profile.likes.each do |like| %>
<%= image_tag like.profile.image.url(), width: 20 %>
<% end %>

Now it will show the profile image that has been liked instead of the user image of the person who liked the profile.

Here is image, under the social media buttons there is liked button pressed and liked.


Thanks Chris, this episode is so helpful!


Hi Chris, I implemented this liking posts and it was working fine until I added Friendly_ids to the urls.
When adding the friendly_ids , the show action in Posts controller was changed from @post = Post.find(params[:id]) to @post = Post.find_by slug: params[:id]. I have tried replacing post.id under the likes_create/destroy to post.slug but it doesn't work.
Can you please advice on how to fix this?


Hi Chris,

I've got this working well without ajax, but as soon as I add 'remote: true' I get a 404 error in the console. The button is pressed, but doesn't update.

Any ideas as to why this is?


Hi Chris I applied this to a project I'm working on and managed to get it working as per the video, so many thanks. It helped me a great deal, still a newbie.
How would you go about creating a separate view to show all the objects you have liked.


I have this working as per the tutorial but wanted to have a link in my navbar to show all the likes by the user, i.e. my likes, has anyone implemented this tutorial in this way.

All you have to do is add a controller for it and then you can loop through current_user.likes.each do |like| and print them out on the page.

Thanks for the reply Chris, I'm still pretty new with the rails stuff, so wasn't able to get it working after creating a new controller probably something simple maybe I'll work it out. Might be a good follow up video though I know it's such a common thing on most sites to see Favs, Likes etc but there isn't a great deal of resources out there that show all the steps through to completion for newbies like me.


Hi Chris,
Why did you decide to roll your own liking system rather than using `acts_as_votable`?

Main reason is it's such a simple feature, no need to use a gem. Always great to have one less dependency, less likely to break on upgrading Rails versions, etc. And the benefit is I can customize it and use it exactly how I want.


Jeremy Christopher Bray

Hi @excid3:disqus loving gorails. Would be super helpful for newbies if all the screenshots were in the transcript. Was chasing my tail with a stupid relationship problem, until I went to the source files.


Hi Chris, just subscribed, really nice stuff here. Regarding this tutorial, I have a question about caching: how would you implement this like feature without having to use `current_user` as the cache key (which obviously would defeat the purpose of the cache in the first place) ? Would you recommend using a gem like `render_async` to do this?

Well, you can cache the user signed out version and use that everywhere. That will cover a lot of things if your website gets much traffic from search results. The other thing I'd say is to use an AJAX request or just embed the IDs in the HTML somewhere for the logged in users and just have JS update the cache'd snippet visually to reflect whether the user had voted on it.

This way you can use the same cache for literally everyone and then just tweak it visually based upon the current user with a little JS. That's how I'm handling the watchlist and completed states of videos on GoRails now so I can have a single cache for every episode and then update it visually for logged in users.


Hi Chris, it's been a while since you posted this, but I'm having an issue and can't figure it out. Basically when I click "Like" everything seems to be working fine and the counter goes up and the button changes to "unlike". But when I click "Unlike" nothing happens until I go to another page. Any idea why that might be? Thanks in advance!


Great Video. However I'm stuck and would love some help.
I have listings results incoming from an API. On my index view I want to add the like or heart button however when i add

<%= link_to 'Like', listing_like_index_path(@listing), method: :post %> it's just break and it's because I don't have :listing_id

these are my routes for like
resources :listings, only: [:index, :show] do
resources :like, module: :listings
end

I added resources instead of resource because after the click the button I want them to stay on the index view

Any help will be deeply apreciated

solved it by using

<%= link_to 'Like', listing_like_index_path(listing.mls_id), method: :post %>
dumb mistake on my part


Hi Chris, I've been followed you along for a while, (I just changed the account frequently because I change the workplace as well) and I really appreciate your work. I just wonder that do you share the beginning project of each video. Because I've been feeling kind of hard to follow along your video without the beginning project. Hope you can consider this. Thank you so much. Keep up the good work.

Thank you very much!


Hi Chris,

When I make the AJAX request to like a post I receive the following error in the console:

Failed to load resource: the server responded with a status of 500 (Internal Server Error)

The logs show the following error:

Rack app error handling request { POST /posts/7/like }

#<ActionController::UnknownHttpMethod: POST, accepted HTTP methods are OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, VERSION-CONTROL, REPORT, CHECKOUT, CHECKIN, UNCHECKOUT, MKWORKSPACE, UPDATE, LABEL, MERGE, BASELINE-CONTROL, MKACTIVITY, ORDERPATCH, ACL, SEARCH, MKCALENDAR, and PATCH>

Back in the console the error links to the rails-ujs file and shows me the error at the following location:

if (xhr.readyState === XMLHttpRequest.OPENED) {
**  return xhr.send(options.data);
**}

I'm running on Rails 5.2 if that helps.


Hi Chris,

I want to ask you a question, I got stuck at the part of implementing the likes? method in the user controller. Except my method was named saved? as was my table and related files. I kept getting the error unitialized constant Item::Safe, for hours I tried to fix this and my code all seemed fine. I finally just renamed my table and related files to bookmarks instead of saves. After updating all the models, controllers, routes, ect. It worked... Do you have any insight on this? Is it possible to be a naming conflict?

Thanks in advance.

Yeah, if you ever overwrote the save method like with an association, then that would cause problems. Almost certainly was your issue.

"Bookmark" might be an easier name to work with on the backend and then just use a different term for the UI.

Appreciate the speedy response. Thanks again.


Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 27,623+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.