I have a comment resource nested in a post resource.
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(params[:comment])
#comment.ip = request.remote_ip
if #comment.save
redirect_to post_path(#post, notice: "Comment was successfully created")
else
flash[:alert] = "Comment could not be created"
render 'posts/show'
end
end
This all works well enough, but I have a nag item in that when the posts/show page with the comment form re-renders, it shows the comment that didn't pass validation inline. I'd like to know the correct way to do this, short of performing some logic in the view layer to not display a comment that isn't saved.
I ended up solving it in the view, because I couldn't find any other solution
<% #post.comments.each do |c| %>
<% unless c.new_record? %>
<strong><%= "#{c.name} wrote: " %></strong><br />
<blockquote><%= c.body %></blockquote>
<% end %>
<% end %>
Related
flash[:errors] does not show after redirect_to in create method. But if I send another invalid form it shows up on the second and all following redirects. The same goes for flash[:success] when there are no errors.
I've tried using flash.keep in both this and the route I am redirecting to, and the views work since the messages do appear after multiple redirects. I'm wondering if it is because the redirects are to routes that render too?
In Controller:
def create
user = User.create(user_params)
if user.errors.any?
flash[:errors] = user.errors.full_messages
redirect_back(fallback_location: root_path)
else
flash[:success] = "USER SUCCESSFULLY CREATED"
redirect_to root_path
end
end
In Views:
<% if flash[:errors] %>
<% flash[:errors].each do |error| %>
<p style="color:red;"><%= error %></p>
<% end %>
<% end %>
AND:
<% if flash[:success] %>
<p style="color: red;"><%= flash[:success] %></p>
<% end %>
No flash messages are shown after the first redirect. After or more redirects, the flash messages are shown
My questions is regarding section 5.10 of the official Rails guide
I have an articles model with fields Title and Text
article.rb :
class Article < ApplicationRecord
validates :title, presence: true, length: { minimum: 5 }
end
articles_controller.rb :
class ArticlesController < ApplicationController
def index
#articles = Article.all
end
def show
#article = Article.find(params[:id])
end
def new
#article = Article.new
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
The guide says that
#article = Article.new
needs to be added to the new action otherwise otherwise #article would be nil in our view, and calling #article.errors.any? would throw an error. Here is the new.html.erb :
<%= form_with scope: :article, url: articles_path, local: true do |form| %>
<% if #article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#article.errors.count, "error") %> prohibited
this article from being saved:
</h2>
<ul>
<% #article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :text %><br>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
What I am able to understand is when there is an error, it is present in #articles so #article.errors.any? helps in showing the errors when the 'new' view is rendered. It indeed works as expected but what I am not able to understand is #article = Article.new in the new action should reset the #article and the errors should be lost after the user is redirected to new. But somehow, the errors are not lost and are indeed being displayed. How is this happening ?
Both render and redirect are different things.
render Renders the content that will be returned to the browser as the response body.
redirect or redirect_to - Redirect is concerned about telling the browser it needs to make a new request to a different location or to the same location as given in path.
It is clearly mentioned in artcle 5.10
Notice that inside the create action we use render instead of redirect_to when save returns false. The render method is used so that the #article object is passed back to the new template when it is rendered. This rendering is done within the same request as the form submission, whereas the redirect_to will tell the browser to issue another request.
Note: You can read render vs redirect in detail
As per your question
def new
#article = Article.new
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new' # this will add error (validations)
end
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
redirect_to 'new' # this will not add any error as this is new request and #article will initialise again.
new #same as redirect
end
end
Edit: Creating Form Objects with ActiveModel. A form object is an object designed specifically to be passed to form_for
We always check for errors #article.errors.any? it will execute if #article object contains any error messages
Please read form_for doc.
render doesn't run any code in the new method, it just uses the new.html.x view. As such, #article = Article.new is never executed.
If you wanted the code from new run, you would need to actually call that method:
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
new #actually runs the code in the 'new' method
render 'new' # doesn't go anywhere near the new method, just uses its view
end
end
I have created a cart in the products view and want to display an error message
views/products/index.html.erb
<% if #order.errors.any? %>
<div class="error error-success note-shadow">
<% #order.errors.full_messages.each do |msg| %>
<p><%= msg %></p>
<% end %>
</div>
<% end %>
controllers/orders_controller.rb
def create
#order = Order.new(params_slip)
respond_to do |format|
if #order.save
format.html {
redirect_to :back,
notice: 'Order was successfully placed.'
}
else
format.html { redirect_to :back }
end
end
end
I am using the above method trying to display the error messages, but it didn't work out. I supposed that is because my create action is in orders_controller, and I couldn't add the error message to the products view?
The root cause is the #order instance variable is not being carried through the redirection process, so that's the reason you don't see any error messages.
You can put the error messages into flash container
if #order.save
[do something]
else
flash[:order_errors] = #order.errors.full_messages
redirect_to :back
end
<% if flash[:order_error] %>
[display it here]
<% end %>
I'm not sure why you put your form in index.html.erb. You'd better put your form in your new.html.erb.
Try to change:
format.html { redirect_to :back }
Into:
render "index"
I hope this helps you
I am getting this error while i am going to delete a comment associated with a post.
// my CommentsController
def destroy
#post = Post.find(params[:post_id])
#comments = #post.comments.find(params[:comment])
#comments.destroy
redirect_to post_path(#post)
end
// my form::
<p>
<strong><%= comment.commenter %>: </strong>
<%= comment.body %>
<strong> at </strong><%= comment.created_at %>
<%= link_to 'Delete Comments', [comment.post,comment], method: :delete, data: {confirm: 'Are you Sure'} %>
</p>
I am getting the error in ::
#comments = #post.comments.find(params[:comment])
please tell me where i am wrong..
You probably should have:
#comment = #post.comments.find(params[:id])
#comment.destroy
Since by default, nested resource record id is referenced by params[:id] in destroy action.
Use this instead
#post = Post.where(:id => params[:post_id]).first
unless #post.blank?
#comment = #post.comments.where(:id => params[:id]).first
unless #comment.blank?
#comment.destroy
flash[:notice] = "Comment deleted"
redirect_to post_path(#post)
else
flash[:notice] = "Comment not found"
redirect_to post_path(#post)
end
end
The difference between find and where is that, where returns an empty array if no comment is found. But find raise an ActiveRecord::RecordNotFound error. Is it clear? If no comment of that id is present in the table then it will return an empty array, and in second line I have added a condition that if comment is not found don't perform destroy action. I've simply handled the error.
You have to include <%= flash[:notice] %> in view file to see the error/success message
I am having that problem with the gem makevoteable that when the page is loaded the post gets upvoted automatic. Instead of just having a link the user can click and upvote. When the page is reloaded I get the AlreadyVotedError in view. I would prefer a more user friendly error message as "You already voted this post"
My view:
<% #posts.each do |post| %>
<h1><%= post.titel %></h1>
<p><%= post.body_html %></p>
<p><%= link_to 'asdasdasd', current_user.up_vote(post) %>
<% end %>
UPDATE:
My route.rb: match 'stem_op/:id' => 'posts#vote_up', :as => 'stem_op'
My public controller:
def vote_up
#post = Post.find(params[:id])
current_user.up_vote(#post)
flash[:message] = 'Thanks for voting!'
redirect_to post_path(#post)
rescue MakeVoteable::Exceptions::AlreadyVotedError
flash[:error] = 'Already voted!'
redirect_to post_path(#post)
end
My view:
<% #posts.each do |post| %>
<h1><%= post.titel %></h1>
<p><%= post.body_html %></p>
<p><%= link_to 'Stem op', stem_op_path(post.id) %>
</tr>
<% end %>
When I try to vote_up a post I get this error:
Template missing - Do I really need a blank view file?
UPDATE:
def vote_up
#post = Post.find(params[:id])
current_user.up_vote(#post)
flash[:message] = 'Thanks for voting!'
redirect_to post_path(#post)
rescue MakeVoteable::Exceptions::AlreadyVotedError
flash[:error] = 'Already voted!'
redirect_to post_path(#post)
end
Error:
SyntaxError in PostsController#vote_up
C:/Rails/den/app/controllers/posts_controller.rb:103: syntax error, unexpected keyword_end, expecting $end
Yes, current_user.up_vote(post) adds a vote for that user. You need to create a controller action that executes current_user.up_vote(post) and handles the flash message. Then you can link to that action in your view.
Edit to answer comment:
guides.rubyonrails.org/action_controller_overview
In your posts controller I imagine you would want something like:
def upvote
#post = Post.find params[:id]
current_user.upvote(#post)
flash[:message] = 'Thanks for voting!'
redirect_to post_path(#post)
rescue MakeVoteable::Exceptions::AlreadyVotedError
flash[:error] = 'Already voted!'
redirect_to post_path(#post)
end
and in your routes something like:
map.resource :post do
member do
post :upvote
end
end
Your link would become link_to 'Upvote!', upvote_post_url(post), :method => :post