Search code examples
ruby-on-railsform-for

How to associate the comment form with the correct post


I have user/micropost/comment models where users can comment on others' microposts. Under every post a textfield is shown so that users can enter comments however I am struggling to get find the Micropost Id. I assume the issue is in my form_for comments or the controllers but I am not really sure. Would love some help, thanks.

Error: Couldn't find micropost without an ID

Models:

User Model: has many microposts, has many comments
Micropost Model: belongs to user, has many comments
Comment Model: belongs to micropost, belongs to user

User Controller:

def show #(the profile page where all the posts and comments are)
  @user = User.find(params[:id])
  @microposts = @user.microposts.paginate(page: params[:page])
  @micropost  = current_user.microposts.build if signed_in?
  @comments = @micropost.comments
  @comment = current_user.comments.build(:micropost => @micropost) if signed_in?
end

Comment Controller:

def create
  @micropost = Micropost.find(params[:id])
  @comment = current_user.comments.build(:micropost => @micropost) #can someone explain what happens in the parentheses? 
  @comment.user = current_user
  @comment.save
  redirect_to :back
end

View/comments/_comment_form:

<%= form_for(@comment) do |f| %>
  <div id="comment_field">
    <%= f.text_field :content, placeholder: "Say Something..." %>
  </div>
<% end %>

Routes:

resources :users
resources :microposts, only: [:create, :destroy]
resources :comments, only: [:create, :destroy]

Solution

  • just add a hidden field for the micropost_id

    <%= form_for(@comment) do |f| %>
      <%= f.hidden_field :micropost_id, value: @micropost.id %>
      <div id="comment_field">
        <%= f.text_field :content, placeholder: "Say Something..." %>
      </div>
    <% end %>
    

    UPDATE: passing micropost_id without any changes to the controller

    Based on your comments controller, you're finding micropost based on params[:id] which is missing when you submit the form. The code below fixes that. However, I suggest you look at nested resources which will make the controller code prettier and more slick

    <%= form_for @comment do |f| %>
      <%= hidden_field_tag :id, @micropost.id %>
      <div id="comment_field">
        <%= f.text_field :content, placeholder: "Say Something..." %>
      </div>
    <% end %>
    

    or update the action of the form

    <%= form_for @comment, url: comments_path(id: @micropost.id) do |f| %>
      <div id="comment_field">
        <%= f.text_field :content, placeholder: "Say Something..." %>
      </div>
    <% end %>
    

    UPDATE: with edits to the comment controller

    # view
    <%= form_for @comment do |f| %>
      <%= hidden_field_tag :micropost_id, @micropost.id %>
      <div id="comment_field">
        <%= f.text_field :content, placeholder: "Say Something..." %>
      </div>
    <% end %>
    
    # comments_controller.rb
    
    def create
      @micropost = Micropost.find params[:micropost_id]
      @comment = current_user.comments.build
      @comment.micropost = @micropost
      @comment.save
    end