Reputation: 1002
I want to display the validation errors of a new @comment
on the articles\view.html.erb
when it fails to persist a comment through the comments#create
.
TLDR, I have created a blog using the Getting Started tutorial with rails 4.2.6 and ruby 2.2.3.
A blog has many Articles and each Article has many Comments.
Following the tutorial, the form for posting a new comment is in the articles\view.html.erb
and it's persisted in the comments#create
. After posting a new comment it redirects to the articles#show
again.
class Article < ActiveRecord::Base
has_many :comments
validates :title, presence: true, length: { minimum: 5 }
end
class Comment < ActiveRecord::Base
belongs_to :article
end
I use the same view - articles/show.html.erb
- to display an article, its comments and post a new comment
<p><strong>Title:</strong><%= @article.title %></
<p><strong>Text:</strong><%= @article.text %></p>
<h2>Comments</h2>
<% @article.comments.each do |comment| %>
<p><strong>Commenter:</strong><%= comment.commenter %></p>
<p><strong>Comment:</strong><%= comment.body %></p>
<% end %>
<h2>Add a comment:</h2>
<%= form_for([@article, @article.comments.build]) do |f| %>
<p><%= f.label :commenter %><br><%= f.text_field :commenter %></p>
<p><%= f.label :body %><br><%= f.text_area :body %></p>
<p><%= f.submit %></p>
<% end %>
<%= link_to 'Edit', edit_article_path(@article) %> |
<%= link_to 'Back', articles_path %>
through the articles_controller
def show
@article = Article.find(params[:id])
end
and the comments_controller
to post a new comment
def create
@article = Article.find(params[:article_id])
@comment = @article.comments.create(comment_params)
redirect_to article_path(@article)
end
It worked as expected :)
Here is where my problems started...
I decided to go further by adding some validation to the Comment model
class Comment < ActiveRecord::Base
belongs_to :article
validates :commenter, presence: true
validates :body, presence: true
end
and display the comment errors when it fails to save it.
First, I tried to display the errors trough the article model
<% 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 %>
Then I tried with a global var @comment
in articles_controller
def show
@article = Article.find(params[:id])
@comment = @article.comments.build
end
<% if @comment.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@comment.errors.count, "error") %> prohibited
this comment from being saved:</h2>
<ul><% @comment.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %></ul>
</div>
<% end %>
It didn't work too.
As far as I understand it doesn't work that way since when I submit a new comment I redirect to the article show action redirect_to article_path(@article)
- either if it succeeded or not to save the new comment - thus a new comment instance is created, overriding the previous one with its errors.
My best solution was using the flash
property with the error messages. It looks like this
comments_controller
def create
@article = Article.find(params[:article_id])
@comment = @article.comments.create(comments_params)
redirect_to article_path(@article), flash: {new_article_errors: @comments.errors.full_messages}
end
articles/show.html.erb
<% unless flash[:new_comment_errors].nil? %>
<div id="error_explanation">
<h2>
<%= pluralize(flash[:new_comment_errors].count, "error") %> prohibited this comment from begin saved:
</h2>
<ul>
<% flash[:new_comment_errors].each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
But this isn't the best solution too. It displays the errors but loses the values already filled (the form is clear).
Should I need to pass the @comment through the redirect_to? Something like this
comments_controller
def create
@article = Article.find(params[:article_id])
@comment = @article.comments.build(comments_params)
if @comment.save
redirect_to article_path(@article)
else
redirect_to article_path(:comment => params[:comment] )
end
end
article_controller
def show
@article = Article.find(params[:id])
@comment = @article.comments.build
@comment.valid? if params[:comment]
end
Now I'm getting the No route matches
error. Should I create a new action that receives a params[:comment]
? or editing the show route to receive it?
I already read the Nested model validation - errors don't show but didn't help because they suggested to create the new action in the comments_controller
and I don't want that (the new post form needs to be in the article/show.html.erb
).
Nested Model Form Part 1 and Nested Model Form Part 2 didn't help too.
Thank you for your time and help!
Upvotes: 1
Views: 162
Reputation: 36860
Instead of redirecting to the ArticlesController#show
method (which as you've discovered will cause all of your instance variables to be lost) you can, instead, render the show view.
if @comment.save
redirect_to article_path(@article)
else
render :template => 'articles/show'
end
You already have @template
and @comment
instance variables, so they'll be used in the render.
Upvotes: 1