I currently have a Customer form that accepts_nested_attributes_for Features. These features could be any number of things, but to keep this simple, we will assume they are First Name, Last Name and Date of Birth (i’m aware of the issues with this being EAV – if you have comments on this, please see my other question on the subject).
In my controller, I have:
3.times {@customer.features.build}
This works fine, and builds 3 features, which save as expected. What I would like, however, is to specify the feature type for each built feature in the view. My view currently looks like:
<%= customer.fields_for :features do |features_builder| %>
<%= render :partial => "features/fixed_feature", :locals => {:feature => features_builder, :fixed_feature_type => "First Name", :form_actions_visible => false} %>
<% end -%>
As one would expect, this creates 3 First Name fields. What I want is to be able to specify 3 different types of feature, like this:
<%= customer.fields_for :features do |features_builder| %>
<%= render :partial => "features/fixed_feature", :locals => {:feature => features_builder, :fixed_feature_type => "First Name", :form_actions_visible => false} %>
<%= render :partial => "features/fixed_feature", :locals => {:feature => features_builder, :fixed_feature_type => "Last Name", :form_actions_visible => false} %>
<%= render :partial => "features/fixed_feature", :locals => {:feature => features_builder, :fixed_feature_type => "Date of Birth", :form_actions_visible => false} %>
<% end -%>
Clearly the code as written above does not work, as it is trying to create 3 fields for each instance. What it seems that I need is an index on the fields_for loop that will allow me to iterate through and add an if clause to each of the partials, so that it fires only once, and each one fires on a different instance of feature.
Can anyone indicate how best I could do this? After searching, I can not seem to find a way to get the index of the fields_for loop.
EDIT: Per the first comment, for clarity: I would like to have three Features rendered on the page, one for each of the features built by the controller. I would like each feature to be rendered differently (with a different local variable passed for :fixed_feature_type in the code above). I am looking for the best way to do this.
EDIT 2: I’m aware that another way to do this would be to dynamically build the nested attributes in the controller (instead of building 3 at once initially) – is there a way to do this?
EDIT 3: Having spent some time searching for a way to do this dynamically, I have come upon a helper method from a Railscast that looks like:
def link_to_add_fields(name, f, association)
new_object = f.object.class.reflect_on_association(association).klass.new
fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|
render(association.to_s.singularize + "_fields", :f => builder)
end
link_to_function(name, h("add_fields(this, \"#{association}\", \"#{escape_javascript(fields)}\")"))
end
It appears that I may be able to define a helper method like this that will allow me to add various feature fields – I’ll be happy to mark as the answer any suggestions on how to modify this to suit my current situation.
I was able to overcome this by moving the rendering of my partial to a custom helper, which allowed me to build an object dynamically, as required.
EDIT: As requested, my code is as follows:
I have added the following code to my application_helper.rb file:
I am calling this helper like so in my view:
This allows me to add as many features as required for the model (in this case, a person) by simply adding additional items to the array.
Hope this helps!
Edit: ‘features/_fixed_feature.html.erb’
Nothing particularly special about this, just takes the local variable passed in by the helper to render the labels etc. without requiring a “feature type” box and then a value box (it renders only a value box, hence the type is fixed).
Per your comments, if you wanted to add features for different models, you would just call the helper again and change the “customer” part (which is the form builder – more commonly this is ‘f’ but I prefer more semantic naming).