Created
May 14, 2011 02:17
-
-
Save AquaGeek/971646 to your computer and use it in GitHub Desktop.
Rails Lighthouse ticket #3426
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 1b20d98b25241ab4a947dc1315638ed01dd71cb3 Mon Sep 17 00:00:00 2001 | |
From: Elad Meidar <[email protected]> | |
Date: Mon, 26 Oct 2009 00:45:44 -0400 | |
Subject: [PATCH] Added FormHelper#fields_for_with_index and tests | |
--- | |
actionpack/lib/action_view/helpers/form_helper.rb | 109 ++++++++++++++++++++- | |
actionpack/test/template/form_helper_test.rb | 42 ++++++++ | |
2 files changed, 148 insertions(+), 3 deletions(-) | |
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb | |
index 994fac0..f3cbedf 100644 | |
--- a/actionpack/lib/action_view/helpers/form_helper.rb | |
+++ b/actionpack/lib/action_view/helpers/form_helper.rb | |
@@ -482,6 +482,18 @@ module ActionView | |
# Delete: <%= project_fields.check_box :_delete %> | |
# <% end %> | |
# <% end %> | |
+ # | |
+ # You will also be able to use #fields_for_with_index, with 2 arguments: first one | |
+ # is the form builder, and the second one is an index counter for child elements (first one will be 0) | |
+ # | |
+ # <% form_for @person, :url => { :action => "update" } do |person_form| %> | |
+ # ... | |
+ # <% person_form.fields_for_with_index :projects do |project_fields, index| %> | |
+ # <div class="project_field_<%= index %>" > | |
+ # Name: <%= project_fields.text_field :name %> | |
+ # </div> | |
+ # <% end %> | |
+ # <% end %> | |
def fields_for(record_or_name_or_array, *args, &block) | |
raise ArgumentError, "Missing block" unless block_given? | |
options = args.extract_options! | |
@@ -496,9 +508,27 @@ module ActionView | |
end | |
builder = options[:builder] || ActionView::Base.default_form_builder | |
+ | |
yield builder.new(object_name, object, self, options, block) | |
end | |
- | |
+ | |
+ def fields_for_with_index(record_or_name_or_array, *args, &block) | |
+ raise ArgumentError, "Missing block" unless block_given? | |
+ options = args.extract_options! | |
+ | |
+ case record_or_name_or_array | |
+ when String, Symbol | |
+ object_name = record_or_name_or_array | |
+ object = args.first | |
+ else | |
+ object = record_or_name_or_array | |
+ object_name = ActionController::RecordIdentifier.singular_class_name(object) | |
+ end | |
+ | |
+ builder = options[:builder] || ActionView::Base.default_form_builder | |
+ yield builder.new(object_name, object, self, options, block), next_child_index | |
+ end | |
+ | |
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object | |
# assigned to the template (identified by +object+). The text of label will default to the attribute name unless you specify | |
# it explicitly. Additional options on the label tag can be passed as a hash with +options+. These options will be tagged | |
@@ -707,6 +737,13 @@ module ActionView | |
def radio_button(object_name, method, tag_value, options = {}) | |
InstanceTag.new(object_name, method, self, options.delete(:object)).to_radio_button_tag(tag_value, options) | |
end | |
+ | |
+ private | |
+ | |
+ def next_child_index | |
+ @index ||= -1 | |
+ @index += 1 | |
+ end | |
end | |
class InstanceTag #:nodoc: | |
@@ -939,7 +976,7 @@ module ActionView | |
end | |
end | |
- (field_helpers - %w(label check_box radio_button fields_for)).each do |selector| | |
+ (field_helpers - %w(label check_box radio_button fields_for fields_for_with_index)).each do |selector| | |
src = <<-end_src | |
def #{selector}(method, options = {}) # def text_field(method, options = {}) | |
@template.send( # @template.send( | |
@@ -986,7 +1023,43 @@ module ActionView | |
@template.fields_for(name, *args, &block) | |
end | |
+ | |
+ def fields_for_with_index(record_or_name_or_array, *args, &block) | |
+ | |
+ "in Builder#fields_For_with_index" | |
+ if options.has_key?(:index) | |
+ index = "[#{options[:index]}]" | |
+ elsif defined?(@auto_index) | |
+ self.object_name = @object_name.to_s.sub(/\[\]$/,"") | |
+ index = "[#{@auto_index}]" | |
+ else | |
+ index = "" | |
+ end | |
+ if options[:builder] | |
+ args << {} unless args.last.is_a?(Hash) | |
+ args.last[:builder] ||= options[:builder] | |
+ end | |
+ | |
+ case record_or_name_or_array | |
+ when String, Symbol | |
+ if nested_attributes_association?(record_or_name_or_array) | |
+ return fields_for_with_index_with_nested_attributes(record_or_name_or_array, args, block) | |
+ else | |
+ name = "#{object_name}#{index}[#{record_or_name_or_array}]" | |
+ end | |
+ when Array | |
+ object = record_or_name_or_array.last | |
+ name = "#{object_name}#{index}[#{ActionController::RecordIdentifier.singular_class_name(object)}]" | |
+ args.unshift(object) | |
+ else | |
+ object = record_or_name_or_array | |
+ name = "#{object_name}#{index}[#{ActionController::RecordIdentifier.singular_class_name(object)}]" | |
+ args.unshift(object) | |
+ end | |
+ @template.fields_for_with_index(name, *args, &block) | |
+ end | |
+ | |
def label(method, text = nil, options = {}) | |
@template.label(@object_name, method, text, objectify_options(options)) | |
end | |
@@ -1039,7 +1112,27 @@ module ActionView | |
fields_for_nested_model(name, association, args, block) | |
end | |
end | |
+ | |
+ def fields_for_with_index_with_nested_attributes(association_name, args, block) | |
+ name = "#{object_name}[#{association_name}_attributes]" | |
+ association = args.first | |
+ | |
+ if association.respond_to?(:new_record?) | |
+ association = [association] if @object.send(association_name).is_a?(Array) | |
+ elsif !association.is_a?(Array) | |
+ association = @object.send(association_name) | |
+ end | |
+ if association.is_a?(Array) | |
+ explicit_child_index = args.last[:child_index] if args.last.is_a?(Hash) | |
+ association.map do |child| | |
+ fields_for_with_index_nested_model("#{name}[#{explicit_child_index || nested_child_index(name)}]", child, args, block) | |
+ end.join | |
+ elsif association | |
+ fields_for_with_index_nested_model(name, association, args, block) | |
+ end | |
+ end | |
+ | |
def fields_for_nested_model(name, object, args, block) | |
if object.new_record? | |
@template.fields_for(name, object, *args, &block) | |
@@ -1050,7 +1143,17 @@ module ActionView | |
end | |
end | |
end | |
- | |
+ | |
+ def fields_for_with_index_nested_model(name, object, args, block) | |
+ if object.new_record? | |
+ @template.fields_for_with_index(name, object, *args, &block) | |
+ else | |
+ @template.fields_for_with_index(name, object, *args) do |builder, child_index| | |
+ @template.concat builder.hidden_field(:id) | |
+ block.call(builder, child_index) | |
+ end | |
+ end | |
+ end | |
def nested_child_index(name) | |
@nested_child_index[name] ||= -1 | |
@nested_child_index[name] += 1 | |
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb | |
index 357c36d..9785623 100644 | |
--- a/actionpack/test/template/form_helper_test.rb | |
+++ b/actionpack/test/template/form_helper_test.rb | |
@@ -909,6 +909,48 @@ class FormHelperTest < ActionView::TestCase | |
assert_dom_equal expected, output_buffer | |
end | |
+ def test_method_fields_for_with_index | |
+ fields_for_with_index(:post, @post) do |f, index| | |
+ concat f.text_field(:title) | |
+ concat f.text_area(:body) | |
+ concat f.check_box(:secret) | |
+ concat "<div>This is child index #{index}</div>" | |
+ end | |
+ | |
+ expected = | |
+ "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + | |
+ "<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" + | |
+ "<input name='post[secret]' type='hidden' value='0' />" + | |
+ "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" + | |
+ "<div>This is child index 0</div>" | |
+ | |
+ assert_dom_equal expected, output_buffer | |
+ end | |
+ | |
+ def test_method_fields_for_with_index_with_nested_attributes_collection_association | |
+ | |
+ @post.comments = [Comment.new(321), Comment.new] | |
+ | |
+ form_for(:post, @post) do |f| | |
+ concat f.text_field(:title) | |
+ f.fields_for_with_index(:comments) do |cf, index| | |
+ concat cf.text_field(:name) | |
+ concat "<div>This is item index #{index}</div>" | |
+ end | |
+ end | |
+ | |
+ expected = '<form action="http://www.example.com" method="post">' + | |
+ '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + | |
+ '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' + | |
+ '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #321" />' + | |
+ "<div>This is item index 0</div>" + | |
+ '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="new comment" />' + | |
+ "<div>This is item index 1</div>" + | |
+ '</form>' | |
+ | |
+ assert_dom_equal expected, output_buffer | |
+ end | |
+ | |
def test_fields_for_with_index | |
fields_for("post[]", @post) do |f| | |
concat f.text_field(:title) | |
-- | |
1.6.0.2 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment