Search code examples
ruby-on-railsruby-on-rails-5simple-formnested-forms

Creating a nested resource in Rails 5


I am trying to create a nested resource so that products can have notes associated with them. I have set up the associations within the model etc, but when I try to use the form to create a new note, I get the following error:

NoMethodError in Notes#create
Showing /Users/myusername/myapp/app/views/notes/_form.html.erb where line #2 raised:

undefined method `notes_path' for #<#<Class:0x00007fb3630b1ad0>:0x00007fb361eab868>

This is the line it is referring to:

<%= simple_form_for [@product, @note] do |f| %>

This are the new & create actions in the notes controller:

  def new
    @product = Product.find(params[:product_id])
    @note = @product.notes.build
  end

def create
    @note = Note.new(product: @product)

    respond_to do |format|
      if @note.save
        format.html { redirect_to product_notes, notice: 'Note was successfully created.' }
      else
        flash.now[:error] = "It doesnt work"
        render 'new'
      end
    end
  end

and the form partial:

<%= simple_form_for [@product, @note] do |f| %>
  <%= f.error_notification %>
  <%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>

  <div class="form-inputs">
    <%= f.input :content %>
    <%= f.input :author %>
    <%= f.check_box :visible %>
  </div>

  <div class="form-actions">
    <%= f.button :submit %>
  </div>
<% end %>

I am going round in circles with making changes, and cannot seem to find any documentation on nested resources that isn't deprecated. Can anybody assist, please?

Edited to add:

I changed my controller action to something based on PGill's answer and can now get the page to load without an action controller error. However, it now re-renders the new note form, with errors saying that the form fields cannot be blank. They were not blank when I submitted them - what's happening to cause this?

Updated controller action:

def create
    @product = Product.find(params[:product_id])
    @note = @product.notes.new

    respond_to do |format|
      if @note.save
        format.html { redirect_to product_notes_path(@product), notice: 'Note was successfully created.' }
      else
        format.html { render :new, notice: 'Note failed to be created.' }
      end
    end
  end

When I was previously getting errors, it had this as the request parameters, so they are getting passed?

Parameters:

{"utf8"=>"✓",
 "authenticity_token"=>"lotsofletters",
 "note"=>{"content"=>"test", "author"=>"test", "visible"=>"0"},
 "commit"=>"Create Note",
 "product_id"=>"1"}

Solution

  • Referring to your edit; of course you should get empty fields errors because you are creating a new object @note without providing any attributes for it:

      @note = @product.notes.new
    

    it should be like

    @note = @product.notes.build(params[:note])
    

    also take care to provide a sanitizer for note in notes controller :

    private 
    
    def note_params
       params.require(:note).permit(:content, :author, :visible, :product_id)
    end
    

    so your code in create will look like:

    def create
      @product = Product.find(params[:product_id])
      @note = @product.notes.build(note_params)
    
      respond_to do |format|
        if @note.save
          format.html { redirect_to product_notes_path(@product), notice: 'Note was successfully created.' }
        else
          flash.now[:error] = "It doesnt work"
          render 'new'
        end
      end
    end
    
    private 
    
    def note_params
       params.require(:note).permit(:content, :author, :visible, :product_id)
    end