1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Introduce field_name view helper

The `field_name` helper and corresponding `FormBuilder#field_name`
method provide an Action View-compliant way of overriding a form field
element's `[name]` attribute (similar to `field_id` and
`FormBuilder#field_id` introduced in rails/rails#40127[][]).

```ruby
text_field_tag :post, :title, name: field_name(:post, :title, :subtitle)
  # => <input type="text" name="post[title][subtitle]">

text_field_tag :post, :tag, name: field_name(:post, :tag, multiple: true)
  # => <input type="text" name="post[tag][]">

form_for @post do |f|
  f.field_tag :tag, name: f.field_name(:tag, multiple: true)
  # => <input type="text" name="post[tag][]">
end
```

[rails/rails#40127]: https://github.com/rails/rails/pull/40127
This commit is contained in:
Sean Doyle 2021-10-08 12:52:53 -04:00
parent 9f980664fc
commit 37081bf507
6 changed files with 241 additions and 9 deletions

View file

@ -1,3 +1,15 @@
* Introduce the `field_name` view helper, along with the
`FormBuilder#field_name` counterpart:
```ruby
form_for @post do |f|
f.field_tag :tag, name: f.field_name(:tag, multiple: true)
# => <input type="text" name="post[tag][]">
end
```
*Sean Doyle*
* Add `:day_format` option to `date_select`
date_select("article", "written_on", day_format: ->(day) { day.ordinalize })

View file

@ -1746,6 +1746,28 @@ module ActionView
@template.field_id(@object_name, method, *suffixes, index: index)
end
# Generate an HTML <tt>name</tt> attribute value for the given name and
# field combination
#
# Return the value generated by the <tt>FormBuilder</tt> for the given
# attribute name.
#
# <%= form_for @post do |f| %>
# <%= f.text_field :title, name: f.field_name(:title, :subtitle) %>
# <%# => <input type="text" name="post[title][subtitle]">
# <% end %>
#
# <%= form_for @post do |f| %>
# <%= f.field_tag :tag, name: f.field_name(:tag, multiple: true) %>
# <%# => <input type="text" name="post[tag][]">
# <% end %>
#
def field_name(method, *methods, multiple: false, index: @index)
object_name = @options.fetch(:as) { @object_name }
@template.field_name(object_name, method, *methods, index: index, multiple: multiple)
end
##
# :method: text_field
#

View file

@ -114,6 +114,32 @@ module ActionView
end
end
# Generate an HTML <tt>name</tt> attribute value for the given name and
# field combination
#
# Return the value generated by the <tt>FormBuilder</tt> for the given
# attribute name.
#
# <%= text_field_tag :post, :title, name: field_name(:post, :title, :subtitle) %>
# <%# => <input type="text" name="post[title][subtitle]">
#
# <%= text_field_tag :post, :tag, name: field_name(:post, :tag, multiple: true) %>
# <%# => <input type="text" name="post[tag][]">
#
def field_name(object_name, method_name, *method_names, multiple: false, index: nil)
names = method_names.map! { |name| "[#{name}]" }.join
# a little duplication to construct fewer strings
case
when object_name.empty?
"#{method_name}#{names}#{multiple ? "[]" : ""}"
when index
"#{object_name}[#{index}][#{method_name}]#{names}#{multiple ? "[]" : ""}"
else
"#{object_name}[#{method_name}]#{names}#{multiple ? "[]" : ""}"
end
end
# Creates a dropdown selection box, or if the <tt>:multiple</tt> option is set to true, a multiple
# choice selection box.
#

View file

@ -105,15 +105,7 @@ module ActionView
end
def tag_name(multiple = false, index = nil)
# a little duplication to construct fewer strings
case
when @object_name.empty?
"#{sanitized_method_name}#{multiple ? "[]" : ""}"
when index
"#{@object_name}[#{index}][#{sanitized_method_name}]#{multiple ? "[]" : ""}"
else
"#{@object_name}[#{sanitized_method_name}]#{multiple ? "[]" : ""}"
end
@template_object.field_name(@object_name, sanitized_method_name, multiple: multiple, index: index)
end
def tag_id(index = nil)

View file

@ -1659,6 +1659,126 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
def test_form_for_field_name_with_blank_as
form_for(Post.new, as: "") do |form|
concat form.text_field(:title, name: form.field_name(:title))
end
expected = whole_form("/posts", "new_", "new_") do
%(<input id="title" name="title" type="text">)
end
assert_dom_equal expected, output_buffer
end
def test_form_for_field_name_with_blank_as_and_multiple
form_for(Post.new, as: "") do |form|
concat form.text_field(:title, name: form.field_name(:title, multiple: true))
end
expected = whole_form("/posts", "new_", "new_") do
%(<input id="title" name="title[]" type="text">)
end
assert_dom_equal expected, output_buffer
end
def test_form_for_field_name_without_method_names_or_multiple_or_index
form_for(Post.new) do |form|
concat form.text_field(:title, name: form.field_name(:title))
end
expected = whole_form("/posts", "new_post", "new_post") do
%(<input id="post_title" name="post[title]" type="text">)
end
assert_dom_equal expected, output_buffer
end
def test_form_for_field_name_without_method_names_and_multiple
form_for(Post.new) do |form|
concat form.text_field(:title, name: form.field_name(:title, multiple: true))
end
expected = whole_form("/posts", "new_post", "new_post") do
%(<input id="post_title" name="post[title][]" type="text">)
end
assert_dom_equal expected, output_buffer
end
def test_form_for_field_name_without_method_names_and_index
form_for(Post.new, index: 1) do |form|
concat form.text_field(:title, name: form.field_name(:title))
end
expected = whole_form("/posts", "new_post", "new_post") do
%(<input id="post_1_title" name="post[1][title]" type="text">)
end
assert_dom_equal expected, output_buffer
end
def test_form_for_field_name_without_method_names_and_index_and_multiple
form_for(Post.new, index: 1) do |form|
concat form.text_field(:title, name: form.field_name(:title, multiple: true))
end
expected = whole_form("/posts", "new_post", "new_post") do
%(<input id="post_1_title" name="post[1][title][]" type="text">)
end
assert_dom_equal expected, output_buffer
end
def test_form_for_field_name_with_method_names
form_for(Post.new) do |form|
concat form.text_field(:title, name: form.field_name(:title, :subtitle))
end
expected = whole_form("/posts", "new_post", "new_post") do
%(<input id="post_title" name="post[title][subtitle]" type="text">)
end
assert_dom_equal expected, output_buffer
end
def test_form_for_field_name_with_method_names_and_index
form_for(Post.new, index: 1) do |form|
concat form.text_field(:title, name: form.field_name(:title, :subtitle))
end
expected = whole_form("/posts", "new_post", "new_post") do
%(<input id="post_1_title" name="post[1][title][subtitle]" type="text">)
end
assert_dom_equal expected, output_buffer
end
def test_form_for_field_name_with_method_names_and_multiple
form_for(Post.new) do |form|
concat form.text_field(:title, name: form.field_name(:title, :subtitle, multiple: true))
end
expected = whole_form("/posts", "new_post", "new_post") do
%(<input id="post_title" name="post[title][subtitle][]" type="text">)
end
assert_dom_equal expected, output_buffer
end
def test_form_for_field_name_with_method_names_and_multiple_and_index
form_for(Post.new, index: 1) do |form|
concat form.text_field(:title, name: form.field_name(:title, :subtitle, multiple: true))
end
expected = whole_form("/posts", "new_post", "new_post") do
%(<input id="post_1_title" name="post[1][title][subtitle][]" type="text">)
end
assert_dom_equal expected, output_buffer
end
def test_form_for_with_collection_radio_buttons
post = Post.new
def post.active; false; end

View file

@ -211,6 +211,66 @@ class FormTagHelperTest < ActionView::TestCase
assert_equal "post_author_name", value
end
def test_field_name_without_object_name
value = field_name("", :title)
assert_equal "title", value
end
def test_field_name_without_object_name_and_multiple
value = field_name("", :title, multiple: true)
assert_equal "title[]", value
end
def test_field_name_without_method_names_or_multiple_or_index
value = field_name(:post, :title)
assert_equal "post[title]", value
end
def test_field_name_without_method_names_and_multiple
value = field_name(:post, :title, multiple: true)
assert_equal "post[title][]", value
end
def test_field_name_without_method_names_and_index
value = field_name(:post, :title, index: 1)
assert_equal "post[1][title]", value
end
def test_field_name_without_method_names_and_index_and_multiple
value = field_name(:post, :title, index: 1, multiple: true)
assert_equal "post[1][title][]", value
end
def test_field_name_with_method_names
value = field_name(:post, :title, :subtitle)
assert_equal "post[title][subtitle]", value
end
def test_field_name_with_method_names_and_index
value = field_name(:post, :title, :subtitle, index: 1)
assert_equal "post[1][title][subtitle]", value
end
def test_field_name_with_method_names_and_multiple
value = field_name(:post, :title, :subtitle, multiple: true)
assert_equal "post[title][subtitle][]", value
end
def test_field_name_with_method_names_and_multiple_and_index
value = field_name(:post, :title, :subtitle, index: 1, multiple: true)
assert_equal "post[1][title][subtitle][]", value
end
def test_hidden_field_tag
actual = hidden_field_tag "id", 3
expected = %(<input id="id" name="id" type="hidden" value="3" autocomplete="off" />)