Search code examples
ruby-on-railsnested-formsnested-attributes

Pre-defined fields for nested attribute in nested form using checkbox


I have a model Food that has many food_varients. The FoodVarient is a model that will be set by the admin of the system. For Example the food varients could be Hot,Spicy,Extra Cheese, etc. And a food can have these varients.

Since, Food has_many food_varients, I decided to use nested form to allow the admin to create a new food item and also select food_varients that the food might have from the pre-defined food_varients created before-hand by the admin.

This is what my FoodsController looks like:

class FoodsController < DashboardBaseController

  # GET /foods/new
  def new
    @food = current_department.foods.new
    @food.food_varients.build
  end

  # GET /foods/1/edit
  def edit
  end

  def food_params
    params.require(:food).permit(:name, :description,food_varients_attributes[:id,:varient_id])
  end

end

I have also accepted the nested attribute in my Food.rb file as follows:

class Food < ApplicationRecord
 has_many :food_varients, dependent: :destroy
 has_many :varients, through: :food_varients, dependent: :destroy

 accepts_nested_attributes_for :food_varients, reject_if: proc { |attributes| attributes['varient_id'] == "0" }

And this is what my form looks like, to add food:

= form_for @food do |f|
  .field.form-group
    = f.label :name
    = f.text_field :name, class: 'form-control', tabindex: 1
  .field.form-group
    = f.label :description
    = f.text_area :description, class: 'form-control', tabindex: 3
   ........

//**Nested Form Starts from here**
    - current_department.varients.each do |varient|
      = f.fields_for :food_varients do |g|
        = g.label :varient_id, varient.title
        = g.check_box :varient_id,{} ,varient.id

I created the nested form by looping around each instance of the Varient model, that was created by the admin, and giving the admin the options to add the food_varients as the new food item is being created.

Problem:

The create is working fine, and the nested attributes are being saved as expected. But, when i try to edit the food item, the nested form is showing duplicate fields. For Example: If originally the food was set to have varients (sweet, and sour). Now, the edit page of the food item is showing me 4 fields instead of the two, with two checked sweet and sour fields, and two unchecked. sweet and sour fields.

Is there a different approach that i must be trying? Because ever other example that i see, makes the use of text_fields to save the nested attribute dynamically, while I am looping through the already existing varient instance.


Solution

  • You don't need to use nested attributes at all here. Just use the collection helpers along with the varient_ids= setter:

    = form_for @food do |f|
      .field.form-group
        = f.label :name
        = f.text_field :name, class: 'form-control', tabindex: 1
      .field.form-group
        = f.label :description
        = f.text_area :description, class: 'form-control', tabindex: 3
      .field.form-group
        = f.label :varient_ids
        = f.collection_select :varient_ids, Varient.all, :id, :name
    
    class FoodsController < DashboardBaseController
      # GET /foods/new
      def new
        @food = current_department.foods.new
      end
    
      # ...
    
      def food_params
        params.require(:food).permit(:name, :description, varient_ids: [])
      end
    end
    

    Rails will automatically add/destroy rows on the join table. The correct spelling is also Variant:

    variant (plural variants)

    Something that is slightly different from a type or norm.
    - https://en.wiktionary.org/wiki/variant#English