New Discussion

Notifications

You’re not receiving notifications from this thread.

Liking Posts Discussion

96
General

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. ;-)

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 :)

append

has_many :likes

to user.rb

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. :)

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.

I've got the polymorphic likes working, and even have the ajax version of destroy working, but I can not debug why the create.js.erb isn't working. Rails is telling me I have a routing issue, but rake routes is telling me that I do have this route configured....

Here is the error from development.log

ActionController::UrlGenerationError - No route matches {:account_id=>#<Account id: "def2dbdf-b459-490d-ac41-3f1d1230a1fa", name: "New Co", created_at: "2019-12-28 02:05:01", updated_at: "2019-12-28 02:05:01">, :action=>"destroy", :controller=>"pitches/likes", :pitch_id=>#<Pitch id: 2, title: "Shopability", account_id: "def2dbdf-b459-490d-ac41-3f1d1230a1fa", user_id: "584a1cc3-844d-4b47-9281-dfc520a749b1", created_at: "2019-12-28 23:10:17", updated_at: "2019-12-28 23:10:17">}, missing required keys: [:id]:

Here's my routes...

resources :pitches do
      resources :comments, module: :pitches
      resources :likes, module: :pitches, only: [:create, :destroy]
    end

note: that these are scoped to account ( multitenancy)

I changed the create.js.erb to be a basic alert("create.js.erb called"); but it's not being called. My create method within my pitches::likes controller looks like this,


 def create
    @likable.likes.where(user_id: current_user.id, likable_id: @likable.id).first_or_create
    respond_to do |format|
      format.html { redirect_to [current_account, @likable]}
      format.js {render layout: false}
    end
  end

Any ideas on how to get this to work?

I finally found the error, it was an error in config/routes.rb. I should have had a singular resource instead of resources, see updated code,

resources :pitches do
      resources :comments, module: :pitches
      resource :likes, module: :pitches, only: [:create, :destroy]
    end

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.

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.

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.

Kohl Kohlbrenner Kohl Kohlbrenner

@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

Kohl Kohlbrenner Kohl Kohlbrenner

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 %>

Kohl Kohlbrenner Kohl Kohlbrenner

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

Kohl Kohlbrenner Kohl Kohlbrenner

fixed it renamed folder from pins_controller to pins

Kohl Kohlbrenner Kohl Kohlbrenner

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'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.

where is the github?

can i have the source coe for this?

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.

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])

I am also having the 404 issue but it's something to do with routes i'm guessing?

ActionController::RoutingError (uninitialized constant Developments):

and then in the console:

POST http://localhost:3000/developments/1/like [HTTP/1.1 404 Not Found 1343ms]

When i run rake routes I get the following:

new_development_like GET /developments/:development_id/like/new(.:format) developments/likes#new

edit_development_like GET /developments/:development_id/like/edit(.:format) developments/likes#edit

development_like GET /developments/:development_id/like(.:format) developments/likes#show

PATCH /developments/:development_id/like(.:format) developments/likes#update
PUT /developments/:development_id/like(.:format) developments/likes#update
DELETE /developments/:development_id/like(.:format) developments/likes#destroy
POST /developments/:development_id/like(.:format) developments/likes#create

Any feedback is appreciated. Thanks!

Josh Zandman Josh Zandman

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?

Lee Humphreys Lee Humphreys

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.

Lee Humphreys Lee Humphreys

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.

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 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.

The base project is generally using https://github.com/excid3/jumpstart

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.

thanks to all that are participating in this forum, one thing though is how do i make the posts likeable every 24 hours. as in liking the post becomes valid after that time of 24 hrs?

Hi newbie question: Will this work in a Rails 6 project? Thanks

Would be great if the screencast could be updated to factor in changes in Rails

It works on Rails 6 after adding this to
config > webpack > environment.js

const { environment } = require('@rails/webpacker')

const webpack = require('webpack')
environment.plugins.prepend('Provide',
  new webpack.ProvidePlugin({
    $: 'jquery/src/jquery',
    jquery: 'jquery/src/jquery',

    jQuery: 'jquery/src/jquery',
    'window.jQuery': 'jquery'
  })
)

module.exports = environment

i think it will be more interesting if it can be done with Stimulus Reflex

Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 88,834+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.