Skip to content

Commit 37081bf

Browse files
committed
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#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#40127]: rails#40127
1 parent 9f98066 commit 37081bf

File tree

6 files changed

+241
-9
lines changed

6 files changed

+241
-9
lines changed

actionview/CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
* Introduce the `field_name` view helper, along with the
2+
`FormBuilder#field_name` counterpart:
3+
4+
```ruby
5+
form_for @post do |f|
6+
f.field_tag :tag, name: f.field_name(:tag, multiple: true)
7+
# => <input type="text" name="post[tag][]">
8+
end
9+
```
10+
11+
*Sean Doyle*
12+
113
* Add `:day_format` option to `date_select`
214

315
date_select("article", "written_on", day_format: ->(day) { day.ordinalize })

actionview/lib/action_view/helpers/form_helper.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1746,6 +1746,28 @@ def field_id(method, *suffixes, index: @index)
17461746
@template.field_id(@object_name, method, *suffixes, index: index)
17471747
end
17481748

1749+
# Generate an HTML <tt>name</tt> attribute value for the given name and
1750+
# field combination
1751+
#
1752+
# Return the value generated by the <tt>FormBuilder</tt> for the given
1753+
# attribute name.
1754+
#
1755+
# <%= form_for @post do |f| %>
1756+
# <%= f.text_field :title, name: f.field_name(:title, :subtitle) %>
1757+
# <%# => <input type="text" name="post[title][subtitle]">
1758+
# <% end %>
1759+
#
1760+
# <%= form_for @post do |f| %>
1761+
# <%= f.field_tag :tag, name: f.field_name(:tag, multiple: true) %>
1762+
# <%# => <input type="text" name="post[tag][]">
1763+
# <% end %>
1764+
#
1765+
def field_name(method, *methods, multiple: false, index: @index)
1766+
object_name = @options.fetch(:as) { @object_name }
1767+
1768+
@template.field_name(object_name, method, *methods, index: index, multiple: multiple)
1769+
end
1770+
17491771
##
17501772
# :method: text_field
17511773
#

actionview/lib/action_view/helpers/form_tag_helper.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,32 @@ def field_id(object_name, method_name, *suffixes, index: nil)
114114
end
115115
end
116116

117+
# Generate an HTML <tt>name</tt> attribute value for the given name and
118+
# field combination
119+
#
120+
# Return the value generated by the <tt>FormBuilder</tt> for the given
121+
# attribute name.
122+
#
123+
# <%= text_field_tag :post, :title, name: field_name(:post, :title, :subtitle) %>
124+
# <%# => <input type="text" name="post[title][subtitle]">
125+
#
126+
# <%= text_field_tag :post, :tag, name: field_name(:post, :tag, multiple: true) %>
127+
# <%# => <input type="text" name="post[tag][]">
128+
#
129+
def field_name(object_name, method_name, *method_names, multiple: false, index: nil)
130+
names = method_names.map! { |name| "[#{name}]" }.join
131+
132+
# a little duplication to construct fewer strings
133+
case
134+
when object_name.empty?
135+
"#{method_name}#{names}#{multiple ? "[]" : ""}"
136+
when index
137+
"#{object_name}[#{index}][#{method_name}]#{names}#{multiple ? "[]" : ""}"
138+
else
139+
"#{object_name}[#{method_name}]#{names}#{multiple ? "[]" : ""}"
140+
end
141+
end
142+
117143
# Creates a dropdown selection box, or if the <tt>:multiple</tt> option is set to true, a multiple
118144
# choice selection box.
119145
#

actionview/lib/action_view/helpers/tags/base.rb

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,7 @@ def add_default_name_and_id(options)
105105
end
106106

107107
def tag_name(multiple = false, index = nil)
108-
# a little duplication to construct fewer strings
109-
case
110-
when @object_name.empty?
111-
"#{sanitized_method_name}#{multiple ? "[]" : ""}"
112-
when index
113-
"#{@object_name}[#{index}][#{sanitized_method_name}]#{multiple ? "[]" : ""}"
114-
else
115-
"#{@object_name}[#{sanitized_method_name}]#{multiple ? "[]" : ""}"
116-
end
108+
@template_object.field_name(@object_name, sanitized_method_name, multiple: multiple, index: index)
117109
end
118110

119111
def tag_id(index = nil)

actionview/test/template/form_helper_test.rb

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1659,6 +1659,126 @@ def test_form_for_field_id_with_index
16591659
assert_dom_equal expected, output_buffer
16601660
end
16611661

1662+
def test_form_for_field_name_with_blank_as
1663+
form_for(Post.new, as: "") do |form|
1664+
concat form.text_field(:title, name: form.field_name(:title))
1665+
end
1666+
1667+
expected = whole_form("/posts", "new_", "new_") do
1668+
%(<input id="title" name="title" type="text">)
1669+
end
1670+
1671+
assert_dom_equal expected, output_buffer
1672+
end
1673+
1674+
def test_form_for_field_name_with_blank_as_and_multiple
1675+
form_for(Post.new, as: "") do |form|
1676+
concat form.text_field(:title, name: form.field_name(:title, multiple: true))
1677+
end
1678+
1679+
expected = whole_form("/posts", "new_", "new_") do
1680+
%(<input id="title" name="title[]" type="text">)
1681+
end
1682+
1683+
assert_dom_equal expected, output_buffer
1684+
end
1685+
1686+
def test_form_for_field_name_without_method_names_or_multiple_or_index
1687+
form_for(Post.new) do |form|
1688+
concat form.text_field(:title, name: form.field_name(:title))
1689+
end
1690+
1691+
expected = whole_form("/posts", "new_post", "new_post") do
1692+
%(<input id="post_title" name="post[title]" type="text">)
1693+
end
1694+
1695+
assert_dom_equal expected, output_buffer
1696+
end
1697+
1698+
def test_form_for_field_name_without_method_names_and_multiple
1699+
form_for(Post.new) do |form|
1700+
concat form.text_field(:title, name: form.field_name(:title, multiple: true))
1701+
end
1702+
1703+
expected = whole_form("/posts", "new_post", "new_post") do
1704+
%(<input id="post_title" name="post[title][]" type="text">)
1705+
end
1706+
1707+
assert_dom_equal expected, output_buffer
1708+
end
1709+
1710+
def test_form_for_field_name_without_method_names_and_index
1711+
form_for(Post.new, index: 1) do |form|
1712+
concat form.text_field(:title, name: form.field_name(:title))
1713+
end
1714+
1715+
expected = whole_form("/posts", "new_post", "new_post") do
1716+
%(<input id="post_1_title" name="post[1][title]" type="text">)
1717+
end
1718+
1719+
assert_dom_equal expected, output_buffer
1720+
end
1721+
1722+
def test_form_for_field_name_without_method_names_and_index_and_multiple
1723+
form_for(Post.new, index: 1) do |form|
1724+
concat form.text_field(:title, name: form.field_name(:title, multiple: true))
1725+
end
1726+
1727+
expected = whole_form("/posts", "new_post", "new_post") do
1728+
%(<input id="post_1_title" name="post[1][title][]" type="text">)
1729+
end
1730+
1731+
assert_dom_equal expected, output_buffer
1732+
end
1733+
1734+
def test_form_for_field_name_with_method_names
1735+
form_for(Post.new) do |form|
1736+
concat form.text_field(:title, name: form.field_name(:title, :subtitle))
1737+
end
1738+
1739+
expected = whole_form("/posts", "new_post", "new_post") do
1740+
%(<input id="post_title" name="post[title][subtitle]" type="text">)
1741+
end
1742+
1743+
assert_dom_equal expected, output_buffer
1744+
end
1745+
1746+
def test_form_for_field_name_with_method_names_and_index
1747+
form_for(Post.new, index: 1) do |form|
1748+
concat form.text_field(:title, name: form.field_name(:title, :subtitle))
1749+
end
1750+
1751+
expected = whole_form("/posts", "new_post", "new_post") do
1752+
%(<input id="post_1_title" name="post[1][title][subtitle]" type="text">)
1753+
end
1754+
1755+
assert_dom_equal expected, output_buffer
1756+
end
1757+
1758+
def test_form_for_field_name_with_method_names_and_multiple
1759+
form_for(Post.new) do |form|
1760+
concat form.text_field(:title, name: form.field_name(:title, :subtitle, multiple: true))
1761+
end
1762+
1763+
expected = whole_form("/posts", "new_post", "new_post") do
1764+
%(<input id="post_title" name="post[title][subtitle][]" type="text">)
1765+
end
1766+
1767+
assert_dom_equal expected, output_buffer
1768+
end
1769+
1770+
def test_form_for_field_name_with_method_names_and_multiple_and_index
1771+
form_for(Post.new, index: 1) do |form|
1772+
concat form.text_field(:title, name: form.field_name(:title, :subtitle, multiple: true))
1773+
end
1774+
1775+
expected = whole_form("/posts", "new_post", "new_post") do
1776+
%(<input id="post_1_title" name="post[1][title][subtitle][]" type="text">)
1777+
end
1778+
1779+
assert_dom_equal expected, output_buffer
1780+
end
1781+
16621782
def test_form_for_with_collection_radio_buttons
16631783
post = Post.new
16641784
def post.active; false; end

actionview/test/template/form_tag_helper_test.rb

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,66 @@ def test_field_id_with_nested_object_name
211211
assert_equal "post_author_name", value
212212
end
213213

214+
def test_field_name_without_object_name
215+
value = field_name("", :title)
216+
217+
assert_equal "title", value
218+
end
219+
220+
def test_field_name_without_object_name_and_multiple
221+
value = field_name("", :title, multiple: true)
222+
223+
assert_equal "title[]", value
224+
end
225+
226+
def test_field_name_without_method_names_or_multiple_or_index
227+
value = field_name(:post, :title)
228+
229+
assert_equal "post[title]", value
230+
end
231+
232+
def test_field_name_without_method_names_and_multiple
233+
value = field_name(:post, :title, multiple: true)
234+
235+
assert_equal "post[title][]", value
236+
end
237+
238+
def test_field_name_without_method_names_and_index
239+
value = field_name(:post, :title, index: 1)
240+
241+
assert_equal "post[1][title]", value
242+
end
243+
244+
def test_field_name_without_method_names_and_index_and_multiple
245+
value = field_name(:post, :title, index: 1, multiple: true)
246+
247+
assert_equal "post[1][title][]", value
248+
end
249+
250+
def test_field_name_with_method_names
251+
value = field_name(:post, :title, :subtitle)
252+
253+
assert_equal "post[title][subtitle]", value
254+
end
255+
256+
def test_field_name_with_method_names_and_index
257+
value = field_name(:post, :title, :subtitle, index: 1)
258+
259+
assert_equal "post[1][title][subtitle]", value
260+
end
261+
262+
def test_field_name_with_method_names_and_multiple
263+
value = field_name(:post, :title, :subtitle, multiple: true)
264+
265+
assert_equal "post[title][subtitle][]", value
266+
end
267+
268+
def test_field_name_with_method_names_and_multiple_and_index
269+
value = field_name(:post, :title, :subtitle, index: 1, multiple: true)
270+
271+
assert_equal "post[1][title][subtitle][]", value
272+
end
273+
214274
def test_hidden_field_tag
215275
actual = hidden_field_tag "id", 3
216276
expected = %(<input id="id" name="id" type="hidden" value="3" autocomplete="off" />)

0 commit comments

Comments
 (0)