Luiz Henrique
Luiz Henrique

Reputation: 957

Rails nested form with cocoon. Attributes using model

I am trying to create a nested form which has options and suboptions, both from the same model called Option. Here is the content of the files:

Model:

class Option < ApplicationRecord
  belongs_to :activity
  has_many :option_students
  has_many :students, through: :option_students

  has_many :suboptions,
    class_name: "Option",
    foreign_key: "option_id"

  belongs_to :parent,
    class_name: "Option",
    optional: true,
    foreign_key: "option_id"

  accepts_nested_attributes_for :suboptions,

    reject_if: ->(attrs) { attrs['name'].blank? }

  validates :name, presence: true
end

Controller:

class OptionsController < ApplicationController
  include StrongParamsHolder

  def index
    @options = Option.where(option_id: nil)
  end

  def show
    @option = Option.find(params[:id])
  end

  def new
    @option = Option.new()
    1.times { @option.suboptions.build}
  end

  def create
    @option = Option.new(option_params)
    if @option.save
      redirect_to options_path
    else
      render :new
    end
  end

  def edit
    @option = Option.find(params[:id])
  end

  def update
    @option = Option.find(params[:id])
    if @option.update_attributes(option_params)
      redirect_to options_path(@option.id)
    else
      render :edit
    end
  end

  def destroy
    @option = Option.find(params[:id])
    @option.destroy
    redirect_to options_path
  end

end

_form.html.erb:

<%= form_for @option do |f| %>

  <p>
    <%= f.label :name %><br>
    <%= f.text_field :name %><br>
    <%= f.label :activity %><br>
    <%= select_tag "option[activity_id]", options_for_select(activity_array) %><br>
  </p>

  <div>
    <div id="suboptions">
      <%= f.fields_for :suboptions do |suboption| %>
        <%= render 'suboption_fields', f: suboption %>
      <% end %>

      <div class="links">
        <%= link_to_add_association 'add suboption', f, :suboptions %>
      </div>
    </div>
  </div>

  <p>
    <%= f.submit "Send" %>
  </p>
<% end %>

_suboption_fields.html.erb

<div class="nested-fields">
  <%= f.label :suboption %><br>
  <%= f.text_field :name %>
  <%= link_to_remove_association "X", f %>
</div>

StrongParamsHolder:

def option_params
    params.require(:option).permit(:name, :activity_id, :students_ids => [], suboptions_attributes: [:id, :name])
end

The view is created correctly, but it is not saving. It goes to "render :new" on create controller. I think it should be a problem with the params, but I am not sure what.

Upvotes: 2

Views: 611

Answers (1)

nathanvda
nathanvda

Reputation: 50057

Probably not saving because of a failed validation. If you are using rails 5, the belongs_to is now more strict, and to be able to save nested-params you need to make the connection/relation between association explicit.

So imho it will work if you add the inverse_of to your relations as follows:

has_many :suboptions,
  class_name: "Option",
  foreign_key: "option_id",
  inverse_of: :parent 

belongs_to :parent,
  class_name: "Option",
  optional: true,
  foreign_key: "option_id"
  inverse_of: :suboptions

If another validation is failing, it could also help to list the errors in your form (e.g. something like @option.errors.full_messages.inspect would help :)

As an aside: I would rename the option_id field in the database to parent_id as this more clearly conveys its meaning.

Upvotes: 3

Related Questions