Rails Guide "undefined method 'comments' for nil:NilClass" - ruby-on-rails

I am new to Rails and working through the Getting Started Guide. I read through this similar question about the guide but it doesn't seem relevant.
I am stuck on section 6.3, where we are trying to let users add comments to blog posts. I have added a comments form to the post show view, which was working fine before, but now raises the following error. What is the issue?
NoMethodError in Posts#show
Showing /Users/.../Desktop/Rails Blog/blog/app/views/posts/show.html.erb where line #24 raised:
undefined method `comments' for nil:NilClass
Extracted source (around line #24):
21 <% end %>
22
23 <h2>Add a comment:</h2>
24 <%= form_for([#post, #posts.comments.build]) do |f| %>
25 <p>
26 <%= f.label :commenter %><br />
27 <%= f.text_field :commenter %>
posts_controller.rb:
class PostsController < ApplicationController
def index
#posts = Post.all
end
def new
#post = Post.new
end
def create
#post = Post.new(params[:post].permit(:title, :text))
if #post.save
redirect_to #post
else
render 'new'
end
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update(params[:post].permit(:title, :text))
redirect_to #post
else
render 'edit'
end
end
def show
#post = Post.find(params[:id])
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :text)
end
end
comments_controller:
class CommentsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment].permit(:commenter, :body))
redirect_to post
end
end
posts.show.html.erb:
<p>
<strong>Title:</strong>
<%= #post.title %>
</p>
<p>
<strong>Text:</strong>
<%= #post.text %>
</p>
<h2>Comments</h2>
<% #post.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([#post, #posts.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 'Back', posts_path %>
<%= link_to 'Edit', edit_post_path(#post) %>

You have #posts. The variable is called #post. Drop the "s".

Related

Why is the object nil in this view?

I want to create a new article object, but have this error:
Showing
/Users/levanngoc/code/blog_app/app/views/articles/new.html.erb where
line #3 raised:
undefined method `errors' for nil:NilClass
How can I fix it?
this is my controller:
class ArticlesController < ApplicationController
def index
#article = Article.all
end
def show
#article = Article.find(params[:id])
end
def new
end
def edit
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
def update
end
def destroy
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
this is my view file: new.html.erb
<h1>New Article</h1>
<%= 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 %>
Your new view uses #article but the new action in the controller doesn't create this instance variable. You must change it to:
def new
#article = Article.new
end
You must declare:
#article = Article.new

nested edit path not working

Trying to figure out the edit path within _comment.html.erb file.
Keep getting this error:
ActiveRecord::RecordNotFound in CommentsController#edit
Couldn't find Article with 'id'=#<Comment::ActiveRecord_Associations_CollectionProxy:0x007fcac37359e8>
Don't know how to figure how to write the correct path.
Comments Controller
class CommentsController < ApplicationController
def create
#article = Article.find(params[:article_id])
#comment = #article.comments.create(comment_params)
redirect_to article_path(#article)
end
def show
#article = Article.find(params[:article_id])
#comment = #article.comments.find(params[:id])
end
def edit
#article = Article.find(params[:article_id])
#comment = #article.comments.find(params[:id])
end
def destroy
#article = Article.find(params[:article_id])
#comment = #article.comments.find(params[:id])
#comment.destroy
redirect_to article_path(#article)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
_comment.html.erb
<p>
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
<p>
<%= link_to 'Edit', edit_article_comment_path(#article.comments, comment) %>
</p>
<p>
<%= link_to 'Show', [comment.article, comment] %>
</p>
<p>
<%= link_to 'Destroy Comment', [comment.article, comment],
method: :delete,
data: { confirm: 'Are you sure?' } %>
</p>
routes
resources :articles do
resources :comments
end
How do I write the correct path?
Also, the earlier path I had was this:
<%= link_to 'Edit', edit_article_comment_path(#article, comment) %>
But it would turn up a blank edit form, ie, none of the text boxes had anything filled in ... hence why I tried the other path.
Any help would be appreciated.
Thank you.
Comment _form.html.erb
<%= 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 %>
Article show.html.erb
<p>
<strong>Title:</strong>
<%= #article.title %>
</p>
<p>
<strong>Text:</strong>
<%= #article.text %>
</p>
<h2>Comments</h2>
<%= render #article.comments %>
<h2>Add a comment:</h2>
<%= render 'comments/form' %>
<%= link_to 'Edit', edit_article_path(#article) %> |
<%= link_to 'Back', articles_path %>
There's a problem in _comment.html.erb
<%= link_to 'Edit', edit_article_comment_path(#article.comments, comment) %>
It should be
<%= link_to 'Edit', edit_article_comment_path(#article, comment) %>
But it would turn up a blank edit form, ie, none of the text boxes had anything filled in
You should check if you are passing the proper values in form_for
<%= form_for [#article, #comment] do |f| %>
EDIT2
Well the problem is you are passing new instance of comment every time, Even with the edit action. That's why you are not getting any values in the form
<%= form_for([#article, #article.comments.build]) do |f| %>
Change it to
<%= form_for([#article, #comment]) do |f| %>
And make sure you are assigning #article and #comment in both new and edit action of CommentsController or from wherever you are rendering the comments/_form
class CommentsController < ApplicationController
def new
#article = Article.find(params[:article_id])
#comment = #article.comments.new
end
def edit
#article = Article.find(params[:article_id])
#comment = #article.comments.find(params[:id])
end
#...
end

ArgumentError in Posts#edit - first argument nil

I am going through the "Getting Started with Rails Tutorial" and am stuck on update aka Edit. It is throwing the ArgumentError in Posts#edit - first argument in form can't be nil or empty. Here is the highlighted line:
First argument in form cannot contain nil or be empty
Extracted source (around line #1):
<%= form_for #post do |f| %>
It seems to have started when I implemented the partial forms part of the tutorial.
Here is the post_contoller, edit action and _forms.html respectively:
Post_controller:
class PostsController < ApplicationController
def new
#post = Post.new
end
def create
#post = Post.new(params[:post].permit(:title, :text))
if #post.save
redirect_to #post
else
render 'new'
end
end
def show
#post = Post.find(params[:id])
end
def index
#posts = Post.all
end
def update
#post = Post.find(params[:id])
if #post.update(params[:post].permit(:title, :text))
redirect_to #post
else
render 'edit'
end
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :text)
end
end
Edit.html
<h1>Edit post</h1>
<%= render 'form' %>
<%= link_to 'Back', posts_path %>
_form.html
<%= form_for #post do |f| %>
<% if #post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited
this post from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
The error shows an ID of "7" which is the record I am trying to update.
All other functions work (show, new, delete) and BTW "new" uses the same partial form and works fine.
Any help would be much appreciated. Thanks!
U should add this in your _controller.rb
def edit
#post = Post.find(params[:id])
end
you have to pass in the post object as local. You cannot directly access instance variables defined in controller in partial. It will be nil
<h1>Edit post</h1>
<%= render :partial => 'form', :locals => {:post => #post} %>
<%= link_to 'Back', posts_path %>
In the partial
<%= form_for post do |f| %>
check below the private is used for the below function only and write all functions above this:
private
def article_params
params.require(:article).permit(:title, :text)
end

Getting undefined method `errors' for nil:NilClass

I'm doing the Ruby on Rails tutorial at http://guides.rubyonrails.org/getting_started.html
However when I get to "5.11 Adding Some Validation", after adding the error message to display when I try to create a new post from the index view I get
"undefined method `errors' for nil:NilClass"
This is the error line highlighted.
I have already created a new Post in my new action in the controller.
Here is the source
new.html.erb
<h1>New Post</h1>
<%= form_for :post, url: posts_path do |f| %>
<% if #post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
posts_controller.rb
class PostsController < ApplicationController
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
#post= Post.find(params[:id])
end
def index
#posts = Post.all
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
private
def post_params
params.require(:post).permit(:title, :text)
end
end
Thanks.
Change
#Post = Post.new
To
#post = Post.new
Subtle but deadly
You've got error in your controller:
def new
#Post = Post.new
end
just change #Post to #post.

What variable should I place in this form to edit a comment (Rails)?

I got two models: Post and Comment. Comment is a nested resource of Post:
routes.rb:
resources :posts do
resources :comments
end
I'm enabling users to edit the comments that are displayed in the post show view:
posts/show.hmtl.erb:
<%= render #comments %>
comments/_comment.html.erb:
<%= link_to "Edit Post Comment", edit_post_comment_path(#post, comment) %>
This form:
comments/_form.html.erb:
<h4>Add a comment:</h4>
<%= form_for([#post, #post.comments.build]) do |f| %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<% unless current_user == nil %>
<% if current_user.id == #post.user_id %>
<%= link_to 'Edit', edit_post_path(#post) %> |
<% end %>
<% end %>
<%= link_to 'Back', posts_path %>
Is for creating a comment in the show view.
I need another form to edit the comment in a new template:
comments/edit.html.erb:
<h1>Edit comment</h1>
<%= render 'form2' %>
<%= link_to 'Back', posts_path %>
comments/form2.html.erb:
<h4>Edit comment:</h4>
<%= form_for() do |f| %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<%= link_to 'Back', posts_path %>
Not sure what to place here:
<%= form_for(HERE) do |f| %>
Any suggestions?
EDIT
comments_controller.rb:
class CommentsController < ApplicationController
def show
#comment = Comment.find(params[:id])
end
def new
#comment = Comment.new
end
def edit
#comment = Comment.find(params[:id])
end
def update
#post = Post.find(params[:post_id])
#comment = Comment.find(params[:id])
#comment.update_attributes(params[:comment])
redirect_to #post
end
def create
#post = Post.find(params[:post_id])
comment_attr = params[:comment].merge :user_id => current_user.id
#comment = #post.comments.create(comment_attr)
redirect_to post_path(#post)
end
end
Comments are nested resources and that means a comment is always tied to a particular Post. In the edit action you'll get both comment_id and post_id as parameters. You should load both comment and post by those id's. Don't build the comment in the form to reuse the form for both new and edit actions. Change your form to:
comments/_form.html.erb
<%= form_for([#post, #comment]) do |f| %>
comments_controller.rb
def edit
#comment = Comment.find(params[:id])
#post = Post.find(params[:post_id])
end
and in the posts_controller.rb
def show
#post = Post.find(params[:id])
#comment = #post.comments.build
end

Resources