简体   繁体   中英

Posting comments from the show action of another controller

I have a typical blog app where users can post comments from the Posts#show template.

resources :posts do
  resources :comments
end

# posts_controller.rb
def show
  @post = Post.find(params[:id])
  @comment = @post.comments.build
end
# comments_controller.rb
def create
  @post = Post.find(params[:post_id])
  @comment = @post.comments.build(params[:comment])
  @comment.author = current_user
  if @comment.save
    redirect_to @post
  else
    render 'posts/show'
  end
end

In the view, I'm first checking if there are comments, outputting them, and then I'm displaying a new comment form.

/ posts/show.html.slim
- if @post.comments.any?
  @post.comments.each do |comment|
    p = comment.author.name
...
= form_for [@post, @comment]

If comment validation fails, I get a 'no method name for nil class' error. I think this is because @post.commets.any? returns true since the comment was built through the post association--even though the comment failed validation and did not get saved.

How do you work around this?

When the comment validation fails, comment.author might not be set and hence will be nil. This explains the nil.name error.

You could try something like

@post.comments.each do |comment|
   p = comment.author.try(:name)

OR

@post.comments.each do |comment|
  unless comment.new_record?
    p = comment.author.try(:name)
  end
end

OR

@post.comments.reject{|c| c.new_record?}.each do |comment|
  p = comment.author.try(:name)
end

Change

if @post.comments.any?

To

if @post.comments.blank?

and check again.

Without knowing what your posts and comments models look like, it's a bit hard to pin-point the problem, but here's how I would solve your problem:

# app/models/post.rb
has_many :comments

# app/models/comment.rb
belongs_to :post

# app/controllers/posts_controller.rb
def show
  @post = Post.find params[:id]
end

# app/controllers/comments_controller.rb
def create
  # No need to instantiate the post and build the comment on it here since
  # we're going to set the post_id on the comment form.

  @comment = Comment.new params[:comment]
  if @comment.save
    # ...
  else
    # ...
  end
end

# app/views/posts/show.html.slim
- if @post.comments.any?
  @post.comments.each do |comment|
    # ...

- form_for Comment.new do |f|
  = f.text_field :author
  = f.text_field :body
  # ...
  = f.hidden_field :post_id, @post.id # This passes the comment's related post_id on to the controller.
  = f.submit

I don't know if the Slim section is syntactically correct - I haven't yet used the templating engine.

Please note that this isn't production safe code! I've omitted a lot of formalities in order to keep the example short and (hopefully) to the point.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM