Rails: fields_for with index?

Is there a way (or a way to remove similar functionality) to make fields_for_with_index ?

Example:

 <% f.fields_for_with_index :questions do |builder, index| %> <%= render 'some_form', :f => builder, :i => index %> <% end %> 

Partial rendering should know that the current index is in the fields_for loop.

+94
loops ruby-on-rails fields-for
Jan 31 '11 at 16:32
source share
8 answers

This would actually be a more efficient approach following the Rails documentation:

 <% @questions.each.with_index do |question,index| %> <% f.fields_for :questions, question do |fq| %> # here you have both the 'question' object and the current 'index' <% end %> <% end %> 

C: http://railsapi.com/doc/rails-v3.0.4/classes/ActionView/Helpers/FormHelper.html#M006456

You can also specify the instance to be used:

  <%= form_for @person do |person_form| %> ... <% @person.projects.each do |project| %> <% if project.active? %> <%= person_form.fields_for :projects, project do |project_fields| %> Name: <%= project_fields.text_field :name %> <% end %> <% end %> <% end %> <% end %> 
+86
Apr 13 '11 at 15:08
source share

The answer is quite simple, as the solution is provided in Rails. You can use f.options options. So, inside your rendering of _some_form.html.erb ,

Index may be available:

 <%= f.options[:child_index] %> 

You do not have to do anything.




Update: it seems my answer was not clear enough.

Original HTML file:

 <!-- Main ERB File --> <% f.fields_for :questions do |builder| %> <%= render 'some_form', :f => builder %> <% end %> 

Subform Submitted:

 <!-- _some_form.html.erb --> <%= f.options[:child_index] %> 
+152
Jul 22 '13 at 1:10
source share

In Rails 4.0.2, an index is now included in the FormBuilder object:

http://apidock.com/rails/v4.0.2/ActionView/Helpers/FormHelper/fields_for

For example:

 <%= form_for @person do |person_form| %> ... <%= person_form.fields_for :projects do |project_fields| %> Project #<%= project_fields.index %> ... <% end %> ... <% end %> 
+91
Mar 25 '14 at 16:27
source share

Rails 4+ and Rails 3 (with patch below)

 <%= form_for @person do |person_form| %> <%= person_form.fields_for :projects do |project_fields| %> <%= project_fields.index %> <% end %> <% end %> 

Rails 3 Support

To get f.index to work in Rails 3, you need to add the monkey patch to the project initializers to add this functionality to fields_for

 # config/initializers/fields_for_index_patch.rb module ActionView module Helpers class FormBuilder def index @options[:index] || @options[:child_index] end def fields_for(record_name, record_object = nil, fields_options = {}, &block) fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options? fields_options[:builder] ||= options[:builder] fields_options[:parent_builder] = self fields_options[:namespace] = options[:namespace] case record_name when String, Symbol if nested_attributes_association?(record_name) return fields_for_with_nested_attributes(record_name, record_object, fields_options, block) end else record_object = record_name.is_a?(Array) ? record_name.last : record_name record_name = ActiveModel::Naming.param_key(record_object) end index = if options.has_key?(:index) options[:index] elsif defined?(@auto_index) self.object_name = @object_name.to_s.sub(/\[\]$/,"") @auto_index end record_name = index ? "#{object_name}[#{index}][#{record_name}]" : "#{object_name}[#{record_name}]" fields_options[:child_index] = index @template.fields_for(record_name, record_object, fields_options, &block) end def fields_for_with_nested_attributes(association_name, association, options, block) name = "#{object_name}[#{association_name}_attributes]" association = convert_to_model(association) if association.respond_to?(:persisted?) association = [association] if @object.send(association_name).is_a?(Array) elsif !association.respond_to?(:to_ary) association = @object.send(association_name) end if association.respond_to?(:to_ary) explicit_child_index = options[:child_index] output = ActiveSupport::SafeBuffer.new association.each do |child| options[:child_index] = nested_child_index(name) unless explicit_child_index output << fields_for_nested_model("#{name}[#{options[:child_index]}]", child, options, block) end output elsif association fields_for_nested_model(name, association, options, block) end end end end end 
+14
Apr 25 '16 at 17:03
source share

Checkout Providing a collection of partial files . If your requirement is that the template needs to iterate over the array and display an additional template for each of the elements.

 <%= f.fields_for @parent.children do |children_form| %> <%= render :partial => 'children', :collection => @parent.children, :locals => { :f => children_form } %> <% end %> 

This will display "_children.erb" and pass the local variable "children" to the template to display. The iteration counter will automatically be available to the template with the form name partial_name_counter . In the case of the example above, the template will be sent to children_counter .

Hope this helps.

+7
Jan 31 '11 at 17:12
source share

I don’t see a decent way to do this using the methods provided by Rails, at least not in -v3.2.14

@Sheharyar Naseer refers to hash parameters that can be used to solve the problem, but not as much as I can see in how it seems to suggest.

I did it =>

 <%= f.fields_for :blog_posts, {:index => 0} do |g| %> <%= g.label :gallery_sets_id, "Position #{g.options[:index]}" %> <%= g.select :gallery_sets_id, @posts.collect { |p| [p.title, p.id] } %> <%# g.options[:index] += 1 %> <% end %> 

or

 <%= f.fields_for :blog_posts do |g| %> <%= g.label :gallery_sets_id, "Position #{g.object_name.match(/(\d+)]/)[1]}" %> <%= g.select :gallery_sets_id, @posts.collect { |p| [p.title, p.id] } %> <% end %> 

In my case, g.object_name returns a string similar to this "gallery_set[blog_posts_attributes][2]" for the third rendered field, so I just map the index on that line and use it.




In fact, a cooler (and perhaps a cleaner one?) The way to do this is to pass the lambda and call it to increase.

 # /controller.rb index = 0 @incrementer = -> { index += 1} 

And in the view

 <%= f.fields_for :blog_posts do |g| %> <%= g.label :gallery_sets_id, "Position #{@incrementer.call}" %> <%= g.select :gallery_sets_id, @posts.collect { |p| [p.title, p.id] } %> <% end %> 
+6
Oct 02 '13 at 16:10
source share

I know this is a little late, but I recently had to do this, you can get the index of fields for this

 <% f.fields_for :questions do |builder| %> <%= render 'some_form', :f => builder, :i => builder.options[:child_index] %> <% end %> 

I hope this helps :)

+1
Jul 27 '17 at 0:52
source share

If you want to control indexes, check the index parameter

 <%= f.fields_for :other_things_attributes, @thing.other_things.build do |ff| %> <%= ff.select :days, ['Mon', 'Tues', 'Wed'], index: 2 %> <%= ff.hidden_field :special_attribute, 24, index: "boi" %> <%= end => 

This will create

 <select name="thing[other_things_attributes][2][days]" id="thing_other_things_attributes_7_days"> <option value="Mon">Mon</option> <option value="Tues">Tues</option> <option value="Wed">Wed</option> </select> <input type="hidden" value="24" name="thing[other_things_attributes][boi][special_attribute]" id="thing_other_things_attributes_boi_special_attribute"> 

If the form is submitted, the parameters will contain something like

 { "thing" => { "other_things_attributes" => { "2" => { "days" => "Mon" }, "boi" => { "special_attribute" => "24" } } } 

I had to use the index option to work with multiple dropdowns. Good luck.

0
Dec 16 '16 at 16:56
source share



All Articles