Will Tuttle
Will Tuttle

Reputation: 450

Why does form_for :symbol return to the same page?

I have a form that is used by both my new and edit templates for a model called product. In my controller, for the create and update actions it redirects to @product and then renders "show" upon success. This works fine when the form is declared with `form_for @product do |f|, but if use a symbol instead of instance variable, it tries to POST to the same page. I.E. If i was on the page products/4/edit and I pressed submit on the form, it would give me a routing error that it was trying to POST products/4/edit, which only has a GET route in resources.

Now if I put in the url option "url: products_path" it redirects correctly to products/4 just like if I used form_for @product. Does this mean that using a symbol with form_for isn't going to my controller actions? Why is trying to POST to itself?

Here's the form

<%= form_for @product do |f| %>     <-- Changing this to :product gives routing error
  <% if @product.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@product.errors.count, "error") %> prohibited this product from being saved:</h2>

      <ul>
      <% @product.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :title %><br>
    <%= f.text_field :title %>
  </div>
  <div class="field">
    <%= f.label :description %><br>
    <%= f.text_area :description, rows: 6 %>
  </div>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

and here's my controller actions for update and create:

 def create
    @product = Product.new(product_params)

    respond_to do |format|
      if @product.save
        format.html { redirect_to @product, notice: 'Product was successfully created.' }
        format.json { render :show, status: :created, location: @product }
      else
        format.html { render :new }
        format.json { render json: @product.errors, status: :unprocessable_entity }
      end
    end
  end

  def update
    respond_to do |format|
      if @product.update(product_params)
        format.html { redirect_to @product, notice: 'Product was successfully updated.' }
        format.json { render :show, status: :ok, location: @product }
      else
        format.html { render :edit }
        format.json { render json: @product.errors, status: :unprocessable_entity }
      end
    end
  end

Upvotes: 0

Views: 88

Answers (1)

hraynaud
hraynaud

Reputation: 736

When you use a symbol in the form for it basically tells the form_builder what kind of object your are creating the form for. If there happens to be an instance variable set for example @product then the form is even smart enough to pick up the values when rendering the input variables. However in order for the proper url to path to be determined via rails resourceful routing you need to pass a resource.

@product is not the same as :product. The instance variable reflects a resource in your system so a resourceful route can be generated for it. That is not the case when using the symbol which is why need to set the url param explicitly.

when using a :product the url of the form action is set the url of the current page which is why your submit is going to your edit action.

Upvotes: 1

Related Questions