Deleting with link_to from within a Rails 4 view - ruby-on-rails

The following Rails 4 link_to is wrong, and thus I'm unable to delete and not sure why. In this project, "bookmarks" is a nested resource under "users" so rake routes gives me:
DELETE /users/:user_id/bookmarks/:id(.:format) bookmarks#destroy
View:
<% #bookmarks.each do |bookmark| %>
<%= link_to "delete", user_bookmarks_path(#user, bookmark.id), method: :delete %>
<% end %>
Controller:
def destroy
#user.bookmarks.find(params[:id]).destroy
redirect_to root_path
end
private
def bookmark_params
params.require(:bookmark).permit(:title, :bookmark_url)
end
def get_user
#user = User.friendly.find(params[:user_id])
end
The result is a link that looks like http://www.example.com/users/jane-doe/bookmarks.6 where 6 is the correct ID of the bookmark to be deleted. But I don't understand why it's not creating /bookmarks/6, which I think would then work fine with destroy in my controller. It feels like there's some big conceptual piece I'm just not understanding. Any tips are appreciated.

Your view should look something like this. It appears your path name is incorrect:
<% #bookmarks.each do |bookmark| %>
<%= link_to "delete", user_bookmark_path(#user, bookmark.id), method: :delete %>
<% end %>

Related

RoR - how to make an .each loop on views pass a variable to the controller

Im trying to add votes to comments on my blog application. But i cant seem to be able to send the variable within the below loop to the controller:
<% #comments.each do |c| %>
<h1><%= c.title %></h1>
<p><%= c.content %></p>
<p><%= c.user.username %></p>
<% if current_user.id == c.user_id %>
<%= link_to "Edit", edit_comment_path(#post, c)%>
<%= link_to "Delete", delete_comment_path(#post, c), method: :delete, data: { confirm: "Are you sure?" }%>
<% end %>
<% if current_user.id != c.user_id %>
<p>Review this comment</p>
<%= link_to like_path(c), method: :put do %>
Upvote
<%= c.upvote %>
<% end %>
<%= link_to dislike_path(c), method: :put do %>
Downvote
<%= c.downvote %>
<% end %>
<% end %>
<% end %>
controller:
...
def upvote
object_comment = Comment.find(#comment.id)
object_comment.increment!(:upvote)
redirect_to show_path(#post)
end
def downvote
object_comment = Comment.find(#comment.id)
object_comment.increment!(:downvote)
redirect_to show_path(#post)
end
....
Routes:
Rails.application.routes.draw do
....
#Comments
post '/post/:post_id', to: 'comment#create', as: 'new_comments'
get '/post/:post_id/:comment_id/edit', to: 'comment#edit', as: 'edit_comment'
put '/post/:post_id/comment/:comment_id', to: 'comment#update', as: 'update_comment'
delete '/post/:id/:comment_id/', to: 'comment#destroy', as: 'delete_comment'
put 'like', to: "comment#upvote", as: 'like'
put 'dislike', to: "comment#downvote", as: 'dislike'
end
I would like to receive the 'c' stated on the each loop as a variable on the upvote and downvote methods to replace the #comment within object_comment = Comment.find(#comment.id), in order to increment the votes. is this possible?
As it is right i obviously receive the following error:
NoMethodError (undefined method `id' for nil:NilClass):
Your *_path methods do not know what to do with that single argument. (If you want to pass an object to your links, you might want to research RESTful routing. However, to solve it with your current routes, I suggest
<%= link_to "Downvote #{c.downvote}" dislike_path(id: c.id) %>
It should then be possible to access this id attribute in the controller via params.
def dislike
#comment = Comment.find(params[:id])
...
end
A couple of other things to think about:
It's very unusual to perform a put action on clicking a link. You might want to consider doing this as a get action instead.
Does the downvote method return something which is different for each comment? If not, it's standard practise to include it in the controller's helper.
You can get id in parameters, please check below methods
def upvote
object_comment = Comment.find(params[:id])
object_comment.increment!(:upvote)
redirect_to show_path(#post)
end
def downvote
object_comment = Comment.find(params[:id])
object_comment.increment!(:downvote)
redirect_to show_path(#post)
end
....
I think you're doing it wrong. You'll need to save the user's action on the comment with a model CommentVote for example that belongs to user and comment.
Then you'll need to do something like:
class CommentVote
belongs_to :comment
belongs_to :user
enum type: [:upvote, :downvote]
def self.vote(type:, comment_id:, user_id:)
comment_vote = CommentVote.where(comment_id: comment_id, user_id: user_id).first_or_initialize
comment_vote.type = type
comment_vote.save
end
end
And then do a after_save :update_counts in the CommentVote model:
def update_counts
self.comment.upvote = self.comment.comment_votes.upvote.count
self.comment.downvote = self.comment.comment_votes.downvote.count
self.comment.save
end
and you'll call it like: CommentVote.vote(type: :upvote, comment_id: 1, user_id: 1)

Acts_As_Votable Gem Automatically Liking Everything

I'm using the acts_as_votable gem to like and unlike "Deals" in my Ruby on Rails project. My user is set to act_as_voter and my deal is set to acts_as_votable, but for some reason everything is set to like as soon as a new user is created, and they can't unlike the deal. For some reason my list of deals all have an unlike button and it doesn't actually do anything but refresh the page. Here's some of my code.
app/views/catalog/index.html.erb
<ul class="deals_list">
<% #deals.each do |deal| %>
<li>
<div>
...
<div class="favorite">
<% if account_signed_in? and current_account.accountable_type == "Personnel" %>
<%= image_tag("dark-favorite.png") %>
<% if deal.liked_by current_account %>
<%= link_to unlike_deal_path(deal), method: :put do %>
Unlike
<% end %>
<% else %>
<%= link_to like_deal_path(deal), method: :put do %>
Like
<% end %>
<% end %>
<% end %>
</div>
</li>
<% end %>
</ul>
app/controllers/deals_controller.rb
def like
#deal = Deal.find(params[:id])
#deal.liked_by current_account
redirect_back(fallback_location: catalog_index_url)
end
def unlike
#deal = Deal.find(params[:id])
#deal.unliked_by current_account
redirect_back(fallback_location: catalog_index_url)
end
config/routes.rb
resources :deals do
member do
put 'like', to: "deals#like"
put 'unlike', to: "deals#unlike"
end
end
Be sure and read the entire Readme because you're using the library wrong.
To check if a voter has voted on a model, you can use voted_for?. You can check how the voter voted by using voted_as_when_voted_for.
I zeroed in on your problem because I was expecting to see a "?" after the deal.liked_by call, which would indicate a boolean result (by convention, not always the case).
So use this instead:
<% if current_account.voted_for? deal %>

Couldn't find Item with 'id'=; Destroy action

I needed to customize my controllers so I did this:
routes:
post 'add_item', to: 'walls#create'
delete 'remove_item', to: 'walls#destroy'
destroy action WallsController:
def destroy
#item = Item.find params[:id]
#item.destroy
redirect_to :back
end
view:
<% #items.each do |item| %>
<%= item.name %> <%= link_to "X", remove_item_path(item), method: :delete %>
<% end %>
Create action works just fine but still getting error with destroy action: Couldn't find Item with 'id'=
Many thanks for help
remove_item_path is expecting an item id as argument.
link_to "X", remove_item_path(item.id), method: :delete
This works:
link_to "X", remove_item_path(id: item.id), method: :delete

link to nested comment 'edit' action from parent articles controller 'index

I am trying link to the 'edit' action of a nested Comment from the 'index' action of its parent Articles controller. If no comment exists, then the link will go to the 'new' action.
resources :articles do
resources :comments
end
The problem seems to be how to define #comment in the Articles controller in order to get the proper comment id with the associated article id.
The Articles controller contains:
def index
#articles = Article.all
end
I can accomplish what I want by defining #comment in the View 'index.html.erb' (see below):
<% #articles.each do |article| do %>
<% #comment = current_user.comments.where(article_id: article.id) %>
<% if #comment.empty? %>
<%= link_to "New Comment", new_article_comment_path(article) %>
<% else %>
<% #comment.each do |comment| %>
<%= link_to "Edit Comment", edit_article_comment_path(article, comment) %>
<% end %>
<% end %>
<% end %>
But I would prefer to have #comment defined in the Articles controller. I am not sure how to implement '#comment = current_user.comments.where(article_id: article.id)' in the Articles controller without the id as it's the 'index' action.
Must be something simple I'm missing.
I don't know how it would work inside a controller. But I think it's better to move the method inside a helper anyway and call it from there. methods defined inside a helper are automatically available for your views
you can do this:
def comment(article)
#comment = current_user.comments.where(article_id: article.id)
end
Then your view will look like this:
<% #articles.each do |article| do %>
<% comment(article) %>
....more code....
Like you said, if you move this in the controller, where(article_id: article.id) will trip you as you don't know which article the id is bound to.
EDIT:
if you really want to access the method inside the controller, you can do as this post suggests:
class ArticlesController < ActionController::Base
def comment(article)
#comment = current_user.comments.where(article_id: article.id)
end
helper_method :comment(article)
end
but why go through the trouble when you can easily do this inside a helper.

Rails Link_to with Loop - RESTful routes

A question that I hope you can answer for a Q&A app. Still very new with Rails. It should be fairly simple but there is a small issue that I run into whenever I am trying to create a list of links to show up that point to a RESTful route from a loop.
Here's the code for the controller:
users_controller.rb
def list
#user = User.find(params[:id])
#users = User.find(:all, :select => :name)
end
def quiz
#user = User.find(params[:id])
#users = User.find(:all, :select => :name)
end
Here's the code for the view:
<% if current_user.admin? %>
<ul>
<%- #users.each do |link| %>
<li><%= link_to link.name, quiz_user_path(#user, #quiz) %></li>
<% end %>
</ul>
<% end %>
Here's the code for the route:
resources :users do
get 'quiz', :on => :member
end
What I want to do is generate individual links based on the name of the users and then link to the quiz page for that specific user. I'm pretty sure that something needs to be changed for the code in my view. Right now, all I'm getting is a link that all points to the current user which in this case is user 4. (http://localhost:3000/users/4/quiz)
Thanks for any quick tips to solve this.
Currently you are using the same #user instance variable for each link. Instead you need to use the variable set in your loop. The code below should work as expected:
<% if current_user.admin? %>
<ul>
<%- #users.each do |user| %>
<li><%= link_to user.name, quiz_user_path(user) %></li>
<% end %>
</ul>
<% end %>
I also renamed the variable from link to user, for clarity, since your iterating through users not links.
It's because in your current route map quiz_user_path needs only one variable. Try to remove #quiz.

Resources