Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to maintain the ordering for nested attributes when using accepts_nested_attributes_for in a Rails application

Tags:

Here is the parent model:

class TypeWell < ActiveRecord::Base    ...    has_many :type_well_phases, :dependent => :destroy   accepts_nested_attributes_for :type_well_phases, :reject_if => lambda { |a| a[:phase_id].blank? }, :allow_destroy => true    ... end 

Here is the nested model:

class TypeWellPhase < ActiveRecord::Base    belongs_to :type_well   belongs_to :phase  end 

Here is the Phase model:

class Phase < ActiveRecord::Base   ...    has_many :type_well_phases   ... end 

I add nested records in child table (TypeWellPhases) by copying ALL records from my phases (Phase model) table in the parent model's controller as shown below:

class TypeWellsController < ResourceController    ...   def new     @new_heading = "New Type Well - Computed"     @type_well   = TypeWell.new     initialize_phase_fields   end    private    def initialize_phase_fields     Phase.order("id").all.each do |p|       type_well_phase               = @type_well.type_well_phases.build       type_well_phase.phase_id      = p.id       type_well_phase.gw_heat_value = p.gw_heat_value     end   end   ... end 

I do this because I want to maintain a specific order by the children fields that are added. The part of the code Phase.order("id") is for that since the phases table has these records in a specific order.

After this I use the simple_form_for and simple_fields_for helpers as shown below in my form partial:

= simple_form_for @type_well do |f|     ...     #type_well_phases       = f.simple_fields_for :type_well_phases do |type_well_phase|         = render "type_well_phase_fields", :f => type_well_phase 

Everything works as desired; most of the times. However, sometimes the ordering of Child rows in the form gets messed up after it has been saved. The order is important in this application that is why I explicitly do this ordering in the private method in the controller.

I am using the "cocoon" gem for adding removing child records. I am not sure as to why this order gets messed up sometimes.

Sorry for such a long post, but I wanted to provide all the pertinent details up front.

Appreciate any pointers.

Bharat

like image 227
Bharat Avatar asked May 08 '12 20:05

Bharat


Video Answer


2 Answers

I'll explain you in a more generic way. Say, you have Product and Order models:

= form_for @product do |f|     ...     = f.fields_for :orders do |order_fields|         = order_fields.text_field :name 

If you want your orders to be sorted by name then just sort them :)

Instead of:

    = f.fields_for :orders do |order_fields| 

put:

    = f.fields_for :orders, f.object.orders.order(:name) do |order_fields| 

As you see, the f variable that is a parameter of the block of form_for has method object. It's your @product, so you can fetch its orders via .orders and then apply needed sorting via .order(:name) (sorry for this little confusion: order/orders).

The key to your solution that you can pass sorted orders as the second parameter for fields_for.

P.S. Your using the simple_form gem doesn't affect my solution. It'll work if you add 'simple_' to helpers. Just wanted my answer to be more helpful for others and not too task-related.

like image 109
jdoe Avatar answered Sep 24 '22 22:09

jdoe


If you are using Rails 2.3.14 or older you have to use:

f.fields_for :orders, f.object.orders.all(:order => :name) do |order_fields| 
like image 35
Ailison Carvalho Avatar answered Sep 24 '22 22:09

Ailison Carvalho