Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add method fields_for_with_index to FormHelper

  • Loading branch information...
commit 7c562d5e460d97b18e4f3367b3cfb13401732920 1 parent 54823fe
@jmbejar jmbejar authored
View
19 actionpack/lib/action_view/helpers/form_helper.rb
@@ -555,6 +555,19 @@ def apply_form_for_options!(object_or_array, options) #:nodoc:
# ...
# <% end %>
#
+ # In addition, you may want to have access to the current iteration index.
+ # In that case, you can use a similar method called fields_for_with_index
+ # which receives a block with an extra parameter:
+ #
+ # <%= form_for @person do |person_form| %>
+ # ...
+ # <%= person_form.fields_for_with_index :projects do |project_fields, index| %>
+ # Position: <%= index %>
+ # Name: <%= project_fields.text_field :name %>
+ # <% end %>
+ # ...
+ # <% end %>
+ #
# When projects is already an association on Person you can use
# +accepts_nested_attributes_for+ to define the writer method for you:
#
@@ -1216,6 +1229,12 @@ def #{selector}(method, options = {}) # def text_field(method, options = {})
RUBY_EVAL
end
+ def fields_for_with_index(record_name, record_object = nil, fields_options = {}, &block)
+ index = fields_options[:index] || options[:child_index] || nested_child_index(@object_name)
+ block_with_index = Proc.new{ |obj| block.call(obj, index) }
+ fields_for(record_name, record_object, fields_options, &block_with_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)
fields_options[:builder] ||= options[:builder]
View
125 actionpack/test/template/form_helper_test.rb
@@ -974,6 +974,22 @@ def test_nested_fields_for_with_index_and_parent_fields
assert_dom_equal expected, output_buffer
end
+ def test_nested_fields_for_with_index_with_index_and_parent_fields
+ form_for(@post, :index => 1) do |c|
+ concat c.text_field(:title)
+ concat c.fields_for_with_index('comment', @comment, :index => 1) { |r, comment_index|
+ concat r.text_field(:name, "data-index" => comment_index)
+ }
+ end
+
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', 'put') do
+ "<input name='post[1][title]' size='30' type='text' id='post_1_title' value='Hello World' />" +
+ "<input name='post[1][comment][1][name]' size='30' type='text' id='post_1_comment_1_name' value='new comment' data-index='1' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
def test_form_for_with_index_and_nested_fields_for
output_buffer = form_for(@post, :index => 1) do |f|
concat f.fields_for(:comment, @post) { |c|
@@ -1030,6 +1046,20 @@ def test_nested_fields_for_with_index_radio_button
assert_dom_equal expected, output_buffer
end
+ def test_nested_fields_for_with_index_with_index_radio_button
+ form_for(@post) do |f|
+ concat f.fields_for_with_index(:comment, @post, :index => 5) { |c, index|
+ concat c.radio_button(:title, "hello", "data-index" => index)
+ }
+ end
+
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', 'put') do
+ "<input name='post[comment][5][title]' type='radio' id='post_comment_5_title_hello' value='hello' data-index='5' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
def test_nested_fields_for_with_auto_index_on_both
form_for(@post, :as => "post[]") do |f|
concat f.fields_for("comment[]", @post) { |c|
@@ -1229,6 +1259,29 @@ def test_nested_fields_for_with_existing_records_on_a_nested_attributes_collecti
assert_dom_equal expected, output_buffer
end
+ def test_nested_fields_for_with_index_with_existing_records_on_a_nested_attributes_collection_association
+ @post.comments = Array.new(2) { |id| Comment.new(id + 1) }
+
+ form_for(@post) do |f|
+ concat f.text_field(:title)
+ @post.comments.each do |comment|
+ concat f.fields_for_with_index(:comments, comment) { |cf, index|
+ concat cf.text_field(:name, "data-index" => index)
+ }
+ end
+ end
+
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'put') do
+ '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
+ '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" data-index="0" />' +
+ '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
+ '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" data-index="1" />' +
+ '<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
def test_nested_fields_for_with_existing_records_on_a_nested_attributes_collection_association_with_disabled_hidden_id
@post.comments = Array.new(2) { |id| Comment.new(id + 1) }
@post.author = Author.new(321)
@@ -1256,6 +1309,33 @@ def test_nested_fields_for_with_existing_records_on_a_nested_attributes_collecti
assert_dom_equal expected, output_buffer
end
+ def test_nested_fields_for_with_index_with_existing_records_on_a_nested_attributes_collection_association_with_disabled_hidden_id
+ @post.comments = Array.new(2) { |id| Comment.new(id + 1) }
+ @post.author = Author.new(321)
+
+ form_for(@post) do |f|
+ concat f.text_field(:title)
+ concat f.fields_for(:author) { |af|
+ concat af.text_field(:name)
+ }
+ @post.comments.each do |comment|
+ concat f.fields_for_with_index(:comments, comment, :include_id => false) { |cf, index|
+ concat cf.text_field(:name, 'data-index' => index)
+ }
+ end
+ end
+
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'put') do
+ '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
+ '<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="author #321" />' +
+ '<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' +
+ '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" data-index="0" />' +
+ '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" data-index="1" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
def test_nested_fields_for_with_existing_records_on_a_nested_attributes_collection_association_with_disabled_hidden_id_inherited
@post.comments = Array.new(2) { |id| Comment.new(id + 1) }
@post.author = Author.new(321)
@@ -1377,6 +1457,28 @@ def test_nested_fields_for_with_new_records_on_a_nested_attributes_collection_as
assert_dom_equal expected, output_buffer
end
+ def test_nested_fields_for_with_index_with_new_records_on_a_nested_attributes_collection_association
+ @post.comments = [Comment.new, Comment.new]
+
+ form_for(@post) do |f|
+ concat f.text_field(:title)
+ @post.comments.each do |comment|
+ concat f.fields_for_with_index(:comments, comment) { |cf, index|
+ concat cf.text_field(:name, "data-index" => index)
+ }
+ end
+ end
+
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'put') do
+ '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
+ '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="new comment" data-index="0" />' +
+ '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="new comment" data-index="1" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+
def test_nested_fields_for_with_existing_and_new_records_on_a_nested_attributes_collection_association
@post.comments = [Comment.new(321), Comment.new]
@@ -1399,6 +1501,29 @@ def test_nested_fields_for_with_existing_and_new_records_on_a_nested_attributes_
assert_dom_equal expected, output_buffer
end
+ def test_nested_fields_for_with_index_with_existing_and_new_records_on_a_nested_attributes_collection_association
+ @post.comments = [Comment.new(321), Comment.new]
+
+ form_for(@post) do |f|
+ concat f.text_field(:title)
+ @post.comments.each do |comment|
+ concat f.fields_for_with_index(:comments, comment) { |cf, index|
+ concat cf.text_field(:name, "data-index" => index)
+ }
+ end
+ end
+
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'put') do
+ '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
+ '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #321" data-index="0" />' +
+ '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
+ '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="new comment" data-index="1" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+
def test_nested_fields_for_with_an_empty_supplied_attributes_collection
form_for(@post) do |f|
concat f.text_field(:title)

4 comments on commit 7c562d5

@josevalim
Owner

What if instead of adding a new method, we simply added a .index method in the form object that would return the proper index? Wouldn't that be more expected and simpler?

@jmbejar

Good idea, it would provide the same functionality without adding new methods to the current api.
I had thought other option, having the second parameter index as optional for the block passed to fields_for.
Anyway, having the index method in the form object looks good for me.

@jmbejar

BTW, I could handle it somewhere in the next week... Thank you for your feedback

@josevalim
Owner

Great, I am reverting this meantime.

Please sign in to comment.
Something went wrong with that request. Please try again.