Hi friends,
In my previous blog, we talked about customization of form helpers and how to create forms for external resources through form helpers in Form Helpers in Rails Part-3. Now we will switch to discuss on how to build complex forms in rails.
In some apps, there is complex associations between models and because of some requirements there form structures also need to be complex. For that we can't rely on basic forms that we have looked at so far. Suppose while creating a Car model you might want to add different models associated with car in the same form and for later it should be available to add, edit or remove them. Thus for these requirements your models, views and controllers should be like:
Configuring Controller :
Active records in rails provide accepts_nested_attributes_for, that creates an addresses_attributes= on model, that allows to create, update or destroy the models of the car.
class Car < ActiveRecord::Base
has_many :models
accepts_nested_attributes_for :models
end
class Model < ActiveRecord::Base
belongs_to :car
end
Creating Nested Forms:
Nesting in forms can be done as:
<%= form_for @car do |f| %>
Models:
<ul>
<%= f.fields_for :models do |models_form| %>
<li>
<%= models_form.label :number %>
<%= models_form.text_field :number %>
<%= models_form.label :color %>
<%= models_form.text_field :color %>
...
</li>
<% end %>
</ul>
<% end %>
One thing to be noted is that fields_for in nested form runs for each associated models of car. So if no model is present, it renders nothing. So the common practice is to build one or more empty children of the controller, so that at least one set of field is shown to the user. This can be done as:
def new
@car = Car.new
2.times { @car.models.build}
end
The submitted parameters of the nested forms would be like:
{
'car' => {
'name' => 'Swift',
'models_attributes' => {
'0' => {
'number' => 'evi2',
'color' => 'grey'
},
'1' => {
'number' => 'deZire',
'color' => 'white'
}
}
}
}
If the associated object is already exists, fields_for generates a hidden input with the id of the existing record. You can disable this by passing include_id: false to fields_for.
Controller Changes:
In controller you need to white-list the parameters before saving them into the model.
def create
@car = Car.new(car_params)
end
private
def car_params
params.require(:car).permit(:name, models_attributes: [:id, :number, :color])
end
You can allow user to delete the associated records and also can prevent user to save empty records. Both these can be done as done in the following example:
#In Model
class Car < ActiveRecord::Base
has_many :models
accepts_nested_attributes_for :models, allow_destroy: true,
reject_if: lambda {|attributes| attributes['number'].blank?}
end
#In Form
<%= form_for @car do |f| %>
Models:
<ul>
<%= f.fields_for :models do |models_form| %>
<li>
<%= models_form.check_box :_destroy%>
<%= models_form.label :number %>
<%= models_form.text_field :number %>
...
</li>
<% end %>
</ul>
<% end %>
0 Comment(s)