Liking Posts Discussion
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! :)
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
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.
@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
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 %>
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
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.
@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.