Reputation: 957
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
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