I am working on a form (using SimpleForm) that allows you to edit embedded associations. The problem that I'm running into is that the nested models are subclasses so they are different types with potentially different fields. I'm creating hidden forms for each type of model, and using JavaScript to display the form for the selected type.
FYI, I'm using the following gems:
- Rails 3.2
- Mongoid
- SimpleForm
Here's a simplified example of what I have so far:
class Garage
include Mongoid::Document
embeds_one :vehicle
accepts_nested_attributes_for :vehicle
end
class Vehicle
include Mongoid::Document
embedded_in :garage
attr_accessible :_type
end
class Car < Vehicle
field :car_field
attr_accessible :car_field
end
class Truck < Vehicle
field :truck_field
attr_accessible :truck_field
end
In the console:
> garage = Garage.new
> garage.vehicle = Car.new(car_field: 'something')
> garage.save!
In the form:
= simple_form_for @garage do |f|
= f.input :vehicle do |vehicle_form|
= vehicle_form.input :_type, collection: ['Car', 'Truck']
%span.hide{data:{fields-for:'Car'}}
= vehicle_form.input :car_field
%span.hide{data:{fields-for:'Truck'}}
= vehicle_form.input :truck_field
:coffeescript
$('#garage_vehicle_attributes__type').change ->
type = $(@).find('option:selected').val()
$('[data-fields-for="' + type + '"]').show()
The problem that will occur in this example is that it won't be able to render the truck_field
because Car
does not have a truck_field
method. I'm not sure how to solve this problem besides throwing out any form helpers and managing the html and field values manually. Even after much Googling, I haven't been able to find any examples of this type of form.
How can this problem be solved in a standard, "Rails way" using existing form helpers?
This is one of those situations where directly mapping a form to a model is not ideal. I think a user-filled form map and a persistence model instance are two very distinct concepts.
You might try subclassing Vehicle into a class that is used to accept form data. Then mix in all the extra code you need to handle what is specific to the form. This way, you keep your
Vehicle
model clean. You can also override methods inVehicleFormModel
to work like a factory, to build the correct instance when the object is being created. In your controller, instantiate a VehicleFormModel instead of a Vehicle.I think I had a similar problem, however instead of a
has_one
relationship, I havehas_many
.Basically I used
cocoon
gem to dynamically add fields for each decorated class (such asCar
orTruck
) and then Iaccept_nested_attributes_for :vehicle
. The form is built dynamically and on submit, the parameters go insidevehicle_attributes
.The code looks something like (updated for
has_one
association):Then inside
_vehicle_fields
partial you check what object it is (Car
orTruck
) and you render the correct fields.I think this works quite good, and was exactly what I needed. Hope it helps.
I wrote a longer explanation at: http://www.powpark.com/blog/programming/2014/05/07/rails_nested_forms_for_single_table_inheritance_associations