nested_form
nested_form copied to clipboard
Duplicate fields on 'render' for already saved fields (ex: form validation failed)
My nested_form works fine, EXCEPT when in my controller, I render
(not redirect_to
) a page that contains a nested_form, with some nested_fields already saved in the database. It duplicates (only in the HTML) every nested field that has already been saved. So i end up with two hidden fields with the same ID
Model
class Etude
has_many :echanges, dependent: :destroy
accepts_nested_attributes_for :echanges, :allow_destroy => true
In my controller
def update
if @project.update_attributes(etude_params)
flash[:notice] = "Updated !"
redirect_to @etude
else
flash.now[:alert] = "Ouch, duplicated nested fields"
render 'show'
end
end
def etude_params
params.require(:etude).permit( ...
echanges_attributes: [
:id,
...
:_destroy],
Every nested_fields in my form are more or less like this one (in this case echange(s)
is the name of the association)
<%= nested_form for @etude do |f| %>
<%= render 'somepartial', f: f %>
<!-- _somepartial.html.erb -->
<ul id="echanges-list">
<%= f.fields_for :echanges, :wrapper => false do |echange| %>
<li class="fields ...">
<%= echange.link_to_remove "<i class='glyphicon glyphicon-trash'></i> Supprimer".html_safe, class:'btn btn-xs btn-danger', data: {association: 'echange'} %>
...
</li>
<% end %>
</ul>
<%= f.link_to_add(:echanges, :data => { :target => "#echanges-list" }, class: "btn btn-success") do %>
<i class="glyphicon glyphicon-plus"></i> Ajouter un échange
<% end %>
Before I submit, I have a single field with the invisible id input (that is already saved)
<input id="etude_echanges_attributes_0_id" type="hidden" name="etude[echanges_attributes][0][id]" value="54b44f955374611148080000">
Now When I resubmit this single field, this time with validation errors, I end up with two
<input id="etude_echanges_attributes_0_id" type="hidden" name="etude[echanges_attributes][0][id]" value="54b44f955374611148080000">
<input id="etude_echanges_attributes_1_id" type="hidden" name="etude[echanges_attributes][1][id]" value="54b44f955374611148080000">
...And only validation error messages are shown on the second one
If I had 3 already saved nestd_field before rendernig validation errors, I would've ended up with 6 fields (3 duplicates of the original 3)
Did I miss something ?
Rails 4.2 Mongoid 4 Nested form 0.3.2
Woh this is so weird. The objects in memory are completely messed up.
Let's say I have a project with a task saved in the database. Now if I render the task invalid via a nested_form
and fields_for
and I PATCH the results,
In the controller,
- BEFORE
update_attributes
,@project.tasks #=> [#<Phase _id: 1...]
the object is present once, ok - AFTER
update_attributes
,@project.tasks #=> [#<Phase _id: 1..., #<Phase _id: 1...]
, the object is present twice. Yet@project.tasks.length # 1
, it reports an array size of only one object.
Found the source of my problem. Eager Loading
In my controller, I was actually eager-loading before trying to update the values
@project = Project.include(:steps)
# Load my project and embedded steps
...
@project.update_attributes(project_params_with_embedded_steps)
# Will completely mess things up because @project varibale already contains eager-loaded steps!
Hi there guys, I'm having exactly the same problem Everything works fine, but when I render the :new on failed save, the fields get duplicated.
My controller looks like this tho.. And it seems that somewhere im double building my parent instance but I can't see where.
Any hints?
My controller looks like this
class CocktailsController < ApplicationController
before_action :find_cocktail, only: [:show, :edit, :destroy]
def index
@cocktails = Cocktail.all
end
def show
@dose = Dose.new
end
def new
@cocktail = Cocktail.new
@ingredients = Ingredient.all.map {|ing| [ing.name, ing.id]}
@cocktail.doses.build
end
def create
@cocktail = Cocktail.create(cocktail_params)
if @cocktail.save
redirect_to @cocktail
else
@ingredients = Ingredient.all.map {|ing| [ing.name, ing.id]}
render :new
end
end
def edit; end
def update
if @cocktail.update(cocktail_params)
redirect_to @cocktail
else
render :edit
end
end
def destroy
@cocktail.delete
redirect_to cocktails_path
end
private
def find_cocktail
@cocktail = Cocktail.find(params[:id])
end
def cocktail_params
params.require(:cocktail).permit(:name, doses_attributes:[:description, :ingredient_id])
end
end
And here is the model
class Cocktail < ApplicationRecord
validates :name, presence: true, uniqueness: true
has_many :doses, dependent: :destroy
has_many :ingredients, through: :doses
accepts_nested_attributes_for :doses
end
And this is the view
h2. Make your cocktail!
= simple_form_for(@cocktail) do |f|
= f.input :name
h3. Give your recipe
.doses.form-group
= f.simple_fields_for :doses do |t|
= t.input :description
= t.select :ingredient_id, @ingredients
= f.simple_fields_for :doses do |t|
= t.input :description
= t.select :ingredient_id, @ingredients
br
br
br
.btn.btn-primary id="add-dose" Add another one!
= f.submit 'Create!'
end
You can make this work by only having one simple_fields_for in your view, and building your doses twice. So in your new action something like 2.times { cocktail.doses.build }
This SO answer helped me figure this out as I was having the same issue https://stackoverflow.com/questions/12494008/how-to-use-simple-fields-for-to-create-many-objects-of-the-same-nested-type
The idea from what I understand is how form_builder goes about generating fields for associated models. In this case, when you initially build you only associated one dose to your model, and the form builder generated two inputs for the same model. When a validation failed, there were now two associated models (doses) from the two separate input fields on your cocktail model. Then when it went to render the form again, the first time it encounters simple fields for it will generate fields for BOTH doses associated to your model, and when it got to the second simple fields for, it did it again, totaling four doses on the page, and each time you'd try and submit it would continue this pattern of exponentially increasing!