Question

I have a really simple form that works fine until I try and resubmit. After which, it updates the record instead of creating a new one:

In my view, I have this:

#new_order_form
  = render 'form'

And my form partial:

= semantic_form_for [@location, @order], :remote => true do |f|
  -if @order.errors.any?
    #notice{:class=>'alert alert-block alert-error fade in'}
      %a{:class=>"close", :data=>{:dismiss=>"alert", :href=>"#"}}
        x
      %h3  
        = pluralize(@order.errors.count, "error")
      - @order.errors.full_messages.each do |msg|
        = msg
        %br

  %table{:class => 'table table-bordered', :style=> {width: '50%'}}  
    %tr
      %td= f.text_field :product_order_id, :placeholder => "Order ID"

And in my create.js:

$("#new_order_form").html("<%= escape_javascript(render("form")) %>");

This works fine to create the order and renders the errors if they exist.

The problem is: if I enter another order number without refreshing the page, it updates the previously created record, instead of creating a new one.

Looking at the form code, I can see the submit action is replaced with an update.

I have tried to replace the code with this:

 = semantic_form_for [@location, @order], :remote => true do |f|
   #new_order_form
     = render 'form', :f => f

And this in create.js

$("#new_order_form").html("<%= escape_javascript(render :partial => 'form', :locals => {:f => @order}) %>");

Which doesn't work either and complains about an invalid text_field.

What is the best way to do this? Thanks

Was it helpful?

Solution

I am no expert on this topic but Adam's answer seems to be correct. I assume you have something like below in your controller action:

def create
  @order = Order.new(params[:order])

  if @order.save
    responds_to do |format|
      format.js
      format.html { redirect_to 'xxx' }
    end
  else
    responds_to do |format|
      format.js
      format.html { render :new }
    end
  end
end

if you have the above structure, then the create.js.erb is just passing the @order object that has been modified after @order.save call. You can just re-define it in the passed condition as follows(check line no. 4):

def create
  @order = Order.new(params[:order])

  if @order.save
    @order = Order.new
    responds_to do |format|
      format.js
      format.html { redirect_to 'xxx' }
    end
  else
    responds_to do |format|
      format.js
      format.html { render :new }
    end
  end
end

Now if the @order has successfully been saved, new order will be created and passed. Otherwise the old @order with validation errors will be passed. Hope this will help.

OTHER TIPS

The problem is that your create.js is reading the @order that came from the result of the create operation which is the existing @order hence it becomes an update.

One fix: create a new @order with: @order_new = Order.new in your 'if @order.save' create section.

Now tell your form partial to use order instead of @order with:

semantic_form_for [@location, order], :remote => true do |f|

Update your create.js with:

$("#new_order_form").html("<%= escape_javascript(render 'form', :order => @order_new) %>");

Make sure to update anything that references the form render to also use :order => @order. Eg:

<%= render 'form', :order => @order %>

If your @location needs to be a new instance, then the same rules apply as well.

I used @order_new instead of overwriting @order in case you want to do something with @order in your create.js

Side note (for other viewers): Rails 3.x render method doesn't need the partials => and locals => helpers.

I'm not familiar with semantic_form_for, but I assume it's a superset of the regular form_for.

The way form_for works is that it inspects the model to see if it is persisted and then does the default thing to match that. I personally assumed that calling form_for from an edit action would assume update, but it does not. form_for will assume the action is create for any unpersisted model and update for any persisted model. I'm sure you can override this if you want, but I haven't tried doing it myself.

It sounds like what you may be doing is showing the model and trying to create new models on the same page. (If I'm wrong, please include your controllers because it's hard to tell what you intend to do.) You might need both a @show_order and a @new_order in your controller.

I hope that helps, and if not, maybe you can provide more details about your controllers.

Edit:

Whenever you want to be able to create a new model, you need to make sure your controller calls @new_order = Order.new and make sure form_for is called with the @new_order. The new_ is just a prefix to distinguish between the new one and an old one.

In this case, I'm not sure if the controller was order#show or maybe the successful part of order#create.

For show you'd want something like this:

@order = Order.find(params[:id])
@new_order = Order.new

If you're doing this out of create you probably want:

@order = Order.new(params[:order])

if @order.save
  @new_order = Order.new
  redirect_to ???
else
  render :new
end

All of the above should exactly match the stock controller except for the addition of @new_order. (In other words, don't delete the respond_to and format lines if they're there.)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top