ruby-on-railssimple-formsimple-form-for

Submitting a form with a param to new controller action


Rails 6
bootstrap 4

In my books_controller.rb, I have:

def index
  @books = Book.select(:id, :name)
end

def new
    @book = Book.new
  end

def create
  @book = Book.new(book_params)
  respond_to do |format|
    if @book.save
      format.html { redirect_to @book, notice: 'Book was successfully created.' }
      format.json { render :show, status: :created, location: @book }
    else
      format.html { render :new }
      format.json { render json: @book.errors, status: :unprocessable_entity }
    end
  end
end

I have a table, in my views/books/index.htmlslim, I have:

table.table.table-striped
  thead
    tr
      th Name
  tbody
    - @books.each do |book|
      tr
        td = book.name

What I would like to do, is at the end of the table, add a form, allowing users to add a book, without going to a separate form, something like:

I tried:

= form_with url: new_book_path do |f|
  .form-inputs
    = f.label 'Add Book'
    = f.text_field :book_name
  .form-actions
    = f.submit    

And I also tried:

= simple_form_for(@book) do |f|
  .form-inputs
    = f.input :book_name

But nothing happens, when I click on submit, and I get the following message:

ActionController::RoutingError (No route matches [POST] "/bookd/new"):

I also tried:

= simple_form_for(@book || Book.new) do |f|
  .form-inputs
    = f.input :book_name

But. I am getting a:

ActionView::Template::Error (undefined method `book_name` for #<Book:0x000007f9cce...>);

One other thought, is to make it a link, something like:

= link_to new_book_path, :class => "btn btn-primary btn-lg"

But how do I pass the book_name parameter, and which controller action should it go to?

Here are my routes:

books GET /books(.:format) books#index
POST /books(.:format) books#create
new_book GET /books/new(.:format) books#new
edit_book GET /books/:id/edit(.:format) books#edit
book GET /books/:id(.:format) books#show
PATCH /books/:id(.:format) books#update
PUT /books/:id(.:format) books#update
DELETE /books/:id(.:format) books#destroy

Solution

  • In Rails you don't create resources by posting to the new route- you post to the collection route eg. /books. The new action is just used to create a page with a form on it.

    Its still really puzzling why new_book_path would return /bookd/new but the error is actually correct. new_book_path responds to GET - not POST. This is explained in Rails Routing from the Outside.

    If you actually just use:

    table.table.table-striped
      thead
        tr
          th Name
      tbody
        - @books.each do |book|
          tr
            td = book.name
    
    = simple_form_for(@book || Book.new) do |f|
      .form-inputs
        = f.input :name # book_name is a typo?
    

    It will correctly create a form with action="/books" method="POST".

    I have no idea what your trying to do with this mess:

    = form_with url: new_book_path do |f|
      .form-inputs
        = f.label 'Add Book'
        = f.text_field :book_name
      .form-actions
        = f.submit    
    
        = simple_form_for(@book) do |f|
          .form-inputs
            = f.input :book_name
    

    But none of the HTML specifications allow nested <form> elements and the results can be wildly unpredictable. For example a submit button may submit the inner form or the outer form.