I'm currently working on a project involving users, likes, and posts. I have a like/unlike button that I finally got to work some of the time, but on certain user's profiles when I go to unlike a post, I get thrown this error, which says that it is coming from my destroy action in my likes controller:
ActionController::InvalidAuthenticityToken
I'm using devise, but don't know if that has to do with the cause of the issue.
Right now this is what I'm working with:
<h4>All of <%= #user.email %>'s posts:</h4>
<% #user.posts.order('created_at DESC').each do |post| %>
<li><%= post.content %></li>
<% unless current_user.likes.pluck(:post_id).include?(post.id) %>
<%= form_tag likes_path do %>
<%= hidden_field_tag 'post_id', post.id %>
<%= submit_tag "Like", :class => "like_button" %>
<% end %>
<% else %>
<% like = post.likes.where(user_id: current_user.id).first %>
<div class="unlike_button">
<%= form_tag like_path(like) do %>
<%= hidden_field_tag 'post_id', post.id %>
<%= button_to "Unlike", like_path(post), method: :delete %>
</div>
<% end %>
class LikesController < ApplicationController
def create
#post = Post.find(params[:post_id])
#like = Like.new(user_id: current_user.id, post_id: #post.id)
if #like.save
flash[:success] = "Post Liked!"
redirect_back(fallback_location: root_path)
else
flash[:notice] = "Couldn't like post"
redirect_back(fallback_location: root_path)
end
end
def destroy
#like = Like.find(params[:id])
#like.destroy
flash[:success] = "Post unliked"
redirect_back(fallback_location: root_path)
end
end
class PostsController < ApplicationController
def index
#posts = Post.all
#user = User.find(params[:user_id])
end
def new
#post = Post.new
#user = User.find(params[:user_id])
end
def create
#post = current_user.posts.build(post_params)
if #post.save
flash[:success] = "Posted!"
redirect_to user_path(current_user)
else
flash[:notice] = "Post could not be submitted"
redirect_to users_path
end
end
private
def post_params
params.require(:post).permit(:content)
end
end
There is a comment in application_controller.rb..
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
so ,you may try changing..
protect_from_forgery with: :exception
to this
protect_from_forgery with: :null_session
Hope it helps :)
I think I have figured it out.. At least have gotten it to work. I wasusing a form_for helper as well as button_to helper. I deleted the form_for helper and just stuck with
<%= button_to "Unlike", like_path(like), method: :delete %>
and it is now working
What helps me solve this problem is adding the Forward Slash in the URL
From:
= bootstrap_form_tag url: 'signup_with_phone' do |form|
To:
= bootstrap_form_tag url: '/signup_with_phone' do |form|
Related
I'm trying to make a twitter clone where a tweet displays the username of the user next to it. however getting the above error message and highlighting the first line of my create method. any ideas on how to solve.
I've done the association already.
thanks
class TweetsController < ApplicationController
def index
#tweets = Tweet.all.order("created_at DESC")
#tweet = Tweet.new
end
def show
#tweet = Tweet.find(params[:id])
end
def new
# #tweet = Tweet.new
end
def create
#user = User.find(params[:id])
#tweet = Tweet.new(tweet_params)
#tweet.user = #user
if #tweet.save
redirect_to tweets_path
end
end
private
def tweet_params
params.require(:tweet).permit(:content, :user_id)
end
end
tweets
<%= simple_form_for #tweet, id: "form-submit" do |f| %>
<%= f.input :content %>
<%= f.button :submit, class: "btn btn-danger" %>
<% end %>
<% #tweets.each do |tweet| %>
<ul>
<li>
<%= tweet.content %>
<%= tweet.user.username %>
</li>
</ul>
<% end %>
Your form doesn't have id field, so params[:id] is nil.
params hash does not contain id, because of that you are getting this error.
Just modify your create action as
def create
#user = current_user
#tweet = Tweet.new(tweet_params)
#tweet.user = current_user
if #tweet.save
redirect_to tweets_path
end
end
Or
instead of above you can also pass an user_id from the form.
<%= simple_form_for #tweet, id: "form-submit" do |f| %>
<%= f.input :content %>
<%= f.hidden_field :user_id, value: current_user.try(:id) %>
<%= f.button :submit, class: "btn btn-danger" %>
<% end %>
then modify create action as
def create
#tweet = Tweet.new(tweet_params)
if #tweet.save
redirect_to tweets_path
end
end
NOTE: Assuming that you are using devise gem for authentication
You are defining tweet.users after using tweet_params, since for tweet_params the user is not defined, that's why you are getting this error.
By answer accepted here, most appropriate change could be:
def new
#tweet = current_user.tweets.new
end
def create
#tweet = Tweet.new(tweet_params)
if #tweet.save
redirect_to tweets_path
end
end
nothing else was needed to be done.
Right now I have a like button that allows you to like foods. When you try to unlike the food, I get this error:
The action 'destroy' could not be found for UsersController
I'm not sure why it is looking for the destroy action in the users controller. My only guess is because the button is on the user show page, so I assume it defaults to that controller, but how would I access the delete method from my votes controller?
Shared like form
<% unless current_user.votes.empty? || current_user.votes.pluck(:food_id).include?(food.id) %>
<%= form_for #vote do |f| %>
<%= f.hidden_field 'food_id', food.id %>
<%= f.hidden_field 'user_id', food.user.id %>
<%= f.submit "Vote", :class => "like_button" %>
<% end %>
<% else %>
<% vote = food.votes.where(user_id: current_user.id).first %>
<div class="unlike_button">
<%= button_to "Unlike", vote, method: :delete %>
</div>
<% end %>
class VotesController < ApplicationController
def index
#votes = Vote.all
end
def new
#vote = Vote.new
end
def create
#vote = Vote.new(vote_params)
if #vote.save
puts #vote
flash[:notice] = "Thanks for voting!"
redirect_back(fallback_location: root_path)
else
puts "No"
flash[:notice] = "Something went wrong"
redirect_back(fallback_location: root_path)
end
end
def destroy
#vote = Vote.find(params[:id])
if #vote.destroy!
flash[:notice] = "Unvoted!"
redirect_back(fallback_location: root_path)
end
end
private
def vote_params
params.require(:vote).permit(:food_id, :user_id)
end
end
class Vote < ApplicationRecord
belongs_to :user
belongs_to :food
end
I'm building simple web app where people can share their thought / pictures / so on. It has just two controllers Post, and nested in it Comments. Generally, at the moment all works perfectly, users can add, edit and delete posts the same way as comments. The thing I'm trying to do, and have big troubles with, is possibility to Edit comments without redirecting to the Edit Comment View - so to be able to do it from the "posts#show" level, the same way as comments actually are being created. I think it just would look much more nicer... Here are my:
posts_controller.rb
class PostsController < ApplicationController
before_action :find_post, only: [:show, :edit, :update, :destroy]
def index
#posts = Post.all.order(created_at: :desc)
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
def show
#comments = #post.comments
end
def edit
end
def update
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post.destroy
redirect_to root_path
end
private
def find_post
#post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :description)
end
end
comments_controller.rb
class CommentsController < ApplicationController
before_action :find_post, only: [:create, :edit, :update, :destroy]
def create
#comment = #post.comments.create(comment_params)
if #comment.save
redirect_to #post
else
render 'new'
end
end
def edit
#comment = #post.comments.find(params[:id])
end
def update
#comment = #post.comments.find(params[:id])
#comment.update_attributes(comment_params)
if #comment.save
redirect_to #post
else
render 'edit'
end
end
def destroy
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to #post
end
private
def find_post
#post = Post.find(params[:post_id])
end
def comment_params
params.require(:comment).permit(:content)
end
end
View show.html.haml - for post
%h1 Show page
%h3= #post.title
%p= #post.description
=link_to "Edit memory", edit_post_path
=link_to "Delete memory", post_path, method: :delete
%h4 Share your thoughts about the memory
- #comments.each do |comment|
%p= comment.content
=link_to "Edit thought", edit_post_comment_path(#post, comment)
=link_to "Delete thought", post_comment_path(#post, comment), method: :delete
= simple_form_for([#post, #post.comments.build]) do |c|
= c.input :content
= c.submit "Share thought"
View edit.html.haml - for comments (the one I'd like to get rid of / nest somehow to the view shown above)
= simple_form_for([#post, #comment]) do |c|
= c.input :content
= c.submit "Update thought"
I believe there is a simple solution, however despite the fact that I've been reading a lot about possible solution, for a newbie like me it's still difficult to figure out how this should be programed.
The terminology you're looking for is in-place editing, or inline editing
You'll either need a form or JQuery plugin to make any comment written by current_user (assuming you're using Devise) editable.
I've done this a little (you can sign up for free here, click "profile" and then edit the description):
--
The way you'd want to do it is something like this:
#app/assets/javascripts/application.js
# include x-editable scripts
$(".editable).editable([..options..]);
#app/views/posts/index.html.erb
<% #posts.each do |post| %>
<%= post.body %>
<% post.comments.each do |comment| %>
<% if comment.author == current_user %>
<%= content_tag :div, comment.body, class: "editable" %>
<% end %>
<% end %>
<% end %>
I've forgotten how we implemented the provided example; x-editable seems to be the rage now.
Here's how it works:
Include X-Editable (or other plugin) in your app
X-Editable will check for the presence of a .class or #id on an element
Providing such an element will allow X-Editable to make it editable
X-Editable then sends the completed request to the server, acting as Ajax
Looks like there's an x-editable-rails gem you can use:
#Gemfile
gem `x-editable-rails`
#app/assets/javascripts/application.js
#= require editable/bootstrap-editable
#= require editable/rails
#app/views/posts/index.html.erb
<% #posts.each do |post| %>
<% post.comments.each do |comment| %>
<%= editable comment, :body if comment.author == current_user %>
<% end %>
<% end %>
To me this is a strange one.
I have 2 models (with User), I have post and I have comment.
What I am trying to do is to have the form_for comments in the post#show view.
However for some reason when I try to create a comment I get Couldn't find Post without an ID.
When I look at the request parameters though I see:
{"utf8"=>"✓", "authenticity_token"=>"7bAXF66sghTKAF7b61gu08hElC+O1nR6RoT92tqQGOI=", "comment"=>{"content"=>"ok"}, "commit"=>"Add comment", "action"=>"create", "controller"=>"comments", "post_id"=>"23"}
which clearly shows that it does in-fact get the post_id and that it is in this case the id of 23.
After countless hours I thought that I'd see if you guys have a solution.
My comments_controller.rb:
class CommentsController < ApplicationController
before_action :load_post
def create
#comment = #post.comments.build(params[:content])
#comment.user = current_user
if #comment.save
#comment.create_activity :create, owner: current_user
redirect_to root_url, notice: "Comment was created."
else
render :new
end
def load_post
#post = Post.find(params[:id])
end
end
My posts_controller.rb
def show
#post = Post.find(params[:id])
#comment = Comment.new
end
the partial for the forms comments/_form.rb
<%= form_for [#post, #comment] do |f| %>
<%= f.text_area :content %>
<%= f.submit "Add comment" %>
<% end %>
My routes.rb
resources :posts do
resources :comments
end
my posts/show.html.erb
<%= render #post %>
<h3>New comment</h3>
<%= render 'comments/form' %>
my posts/_post.html.erb
<h2><%= #post.title %></h2>
<p><%= #post.content %></p>
<em>written by <%= #post.user.fullname %></em>
You are receiving "post_id" not as "id"
You can make load_post action as private for security concerns
private
def load_post
#post = Post.find(params[:post_id])
end
Access the content like
def create
#comment = #post.comments.build(params[:comment][:content])
....
I am a begginer in Rails, im following code from a book and i am trying stuff to see if it breaks/works, anyway heres my UserControllers classUserController
class UsersController < ApplicationController
def new
#user = User.new
end
def edit
#user = User.find(params[:id])
end
def show
#user = User.find(params[:id])
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user, :notice => 'Cadastro realizado'
else
render :new
end
end
end
And heres my show.html.erb
<p id="notice"><%=notice%></p>
<h2>Perfil: <%=#user.full_name %></h2>
<ul>
<li>Localização: <%= #user.location %> </li>
<li>Bio: <%= #user.bio %></li>
</ul>
<%= link_to 'Editar Perfil', edit_user_path(#user) %>
<%= link_to 'Mostrar Perfil', show_user_path(#user) %>
My problem is in the last line, when i try to acess this page i get a NomethodError,i am trying to understand why, why can i just change that to #user and the page works?
Try:
<%= link_to 'Mostrar Perfil', user_path(#user) %>
or even just
<%= link_to 'Mostrar Perfil', #user %>
In order to see how to name the routes, open a console and run
rake routes