From 47cbfbb98a2a4ccb6d434998183f24be4e2bb1c1 Mon Sep 17 00:00:00 2001 From: Jorge Bejar Date: Thu, 5 Apr 2012 13:17:11 -0300 Subject: [PATCH] Add index method to FormBuilder. Useful when you use field_for and need to know the index number into the iteration. --- actionpack/CHANGELOG.md | 2 + .../lib/action_view/helpers/form_helper.rb | 27 ++++++++-- actionpack/test/template/form_helper_test.rb | 50 +++++++++++++++++++ 3 files changed, 74 insertions(+), 5 deletions(-) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index a4f5116a5d08c..32630e5ded573 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,5 +1,7 @@ ## Rails 4.0.0 (unreleased) ## +* Add `index` method to FormBuilder class. *Jorge Bejar* + * Remove the leading \n added by textarea on assert_select. *Santiago Pastorino* * Changed default value for `config.action_view.embed_authenticity_token_in_remote_forms` diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 6219a7a924438..3f5829d86ac75 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -673,6 +673,19 @@ def apply_form_for_options!(record, object, options) #:nodoc: # <% end %> # ... # <% end %> + # + # When a collection is used you might want to know the index of each + # object into the array. For this purpose, the index method + # is available in the FormBuilder object. + # + # <%= form_for @person do |person_form| %> + # ... + # <%= person_form.fields_for :projects do |project_fields| %> + # Project #<%= project_fields.index %> + # ... + # <% end %> + # ... + # <% end %> def fields_for(record_name, record_object = nil, options = {}, &block) builder = instantiate_builder(record_name, record_object, options) output = capture(builder, &block) @@ -1038,7 +1051,7 @@ class FormBuilder attr_accessor :object_name, :object, :options - attr_reader :multipart, :parent_builder + attr_reader :multipart, :parent_builder, :index alias :multipart? :multipart def multipart=(multipart) @@ -1076,6 +1089,7 @@ def initialize(object_name, object, template, options, block=nil) end end @multipart = nil + @index = options[:index] || options[:child_index] end (field_helpers - [:label, :check_box, :radio_button, :fields_for, :hidden_field, :file_field]).each do |selector| @@ -1107,12 +1121,14 @@ def fields_for(record_name, record_object = nil, fields_options = {}, &block) end index = if options.has_key?(:index) - "[#{options[:index]}]" + options[:index] elsif defined?(@auto_index) self.object_name = @object_name.to_s.sub(/\[\]$/,"") - "[#{@auto_index}]" + @auto_index end - record_name = "#{object_name}#{index}[#{record_name}]" + + 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 @@ -1250,7 +1266,8 @@ def fields_for_with_nested_attributes(association_name, association, options, bl explicit_child_index = options[:child_index] output = ActiveSupport::SafeBuffer.new association.each do |child| - output << fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index(name)}]", child, options, block) + 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 diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index f13296a175733..3fa3898ec8535 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -1835,6 +1835,56 @@ def test_nested_fields_for_with_child_index_option_override_on_a_nested_attribut assert_dom_equal expected, output_buffer end + def test_nested_fields_for_index_method_with_existing_records_on_a_nested_attributes_collection_association + @post.comments = Array.new(2) { |id| Comment.new(id + 1) } + + form_for(@post) do |f| + expected = 0 + @post.comments.each do |comment| + f.fields_for(:comments, comment) { |cf| + assert_equal cf.index, expected + expected += 1 + } + end + end + end + + def test_nested_fields_for_index_method_with_existing_and_new_records_on_a_nested_attributes_collection_association + @post.comments = [Comment.new(321), Comment.new] + + form_for(@post) do |f| + expected = 0 + @post.comments.each do |comment| + f.fields_for(:comments, comment) { |cf| + assert_equal cf.index, expected + expected += 1 + } + end + end + end + + def test_nested_fields_for_index_method_with_existing_records_on_a_supplied_nested_attributes_collection + @post.comments = Array.new(2) { |id| Comment.new(id + 1) } + + form_for(@post) do |f| + expected = 0 + f.fields_for(:comments, @post.comments) { |cf| + assert_equal cf.index, expected + expected += 1 + } + end + end + + def test_nested_fields_for_index_method_with_child_index_option_override_on_a_nested_attributes_collection_association + @post.comments = [] + + form_for(@post) do |f| + f.fields_for(:comments, Comment.new(321), :child_index => 'abc') { |cf| + assert_equal cf.index, 'abc' + } + end + end + def test_nested_fields_uses_unique_indices_for_different_collection_associations @post.comments = [Comment.new(321)] @post.tags = [Tag.new(123), Tag.new(456)]