In the popular Rails gem datagrid, specifying a range filter (for integers) produces a HTML like this:
<form class="datagrid-form artists_grid" id="new_artists_grid" action="/en/artists" accept-charset="UTF-8" method="get">
<div class="datagrid-filter filter">
<label for="artists_grid_birth_year">Year</label>
<input class="birth_year integer_filter from" multiple="multiple" value="" type="text" name="artists_grid[birth_year][]">
<span class="separator integer"> - </span>
<input class="birth_year integer_filter to" multiple="multiple" value="" type="text" name="artists_grid[birth_year][]">
</div>
The W3C validator complains about the HTML, telling
Attribute multiple not allowed on element input at this point.
Indeed, the summary table for the input element for the HTML Living Standard on the W3C website does show that the multiple
attribute is allowed only for types of file
and email
.
But, it seems at least browsers of Firefox and Chrome handle the above-mentioned form as the datagrid
intended and returns an Array for the multiple-text-input fields.
My question is
input type="text" multiple="multiple"
fields as multiple fields and return an Array?
datagrid
to produce a HTML5-valid HTML?Here is the detail of how the above-mentioned HTML is produced.
I specified the following filter on the datagrid for the index table for the Artist model, which has the column (attribute) birth_year
:
filter(:birth_year, :integer, range: true, header: 'Year')
Inputting two values of 1960 and 2010
results in the following URI string with GET parameters:
http://localhost:3000/en/artists?artists_grid%5Bbirth_year%5D%5B%5D=1960&artists_grid%5Bbirth_year%5D%5B%5D=2010&commit=Apply
or, if decoded,
http://localhost:3000/en/artists?artists_grid[birth_year][]=1960&artists_grid[birth_year][]=2010&commit=Apply
So, the browser (Firefox or Chrome) does interpret the two fields as multiple input fields for a common label.
Rails Datagrid gem is now upgraded to Version 2.0 as of November 2024, in which this W3C-HTML violation issue with Range filters is handled properly and fundamentally. The background was discussed in its Issue #325 prior to and during the upgrade.
multiple
is just a side effect of how rails form helpers work.
Normally, duplicate keys get overwritten by the last key:
>> Rack::Utils.parse_nested_query("a=1&a=2")
=> {"a"=>"2"}
But if input name ends with []
it is parsed as array:
>> Rack::Utils.parse_nested_query("a[]=1&a[]=2")
=> {"a"=>["1", "2"]}
A simple way to get []
for input name is to add multiple: true
, which is what datagrid is doing:
<%= text_field :artists_grid, :birth_year, multiple: true %>
#=> <input multiple="multiple" type="text" name="artists_grid[birth_year][]" id="artists_grid_birth_year">
# ^^
To do the same but without multiple
attribute is a bit more awkward:
<%= text_field_tag "artists_grid[birth_year][]" %>
#=> <input type="text" name="artists_grid[birth_year][]" id="artists_grid_birth_year_">
<%= text_field "artists_grid[birth_year]", nil %>
#=> <input type="text" name="artists_grid[birth_year][]" id="artists_grid_birth_year_">
<%= form_with url: "/", method: :get, scope: :artists_grid do |f| %>
<%= f.fields_for :birth_year do |fff| %>
<%= fff.text_field nil %>
#=> <input type="text" name="artists_grid[birth_year][]" id="artists_grid_birth_year_">
<% end %>
<% end %>
To really fix it, it has to be done in rails, somewhere here:
https://github.com/rails/rails/blob/v7.1.3.3/actionview/lib/action_view/helpers/tags/text_field.rb#L17
diff --git a/actionview/lib/action_view/helpers/tags/text_field.rb b/actionview/lib/action_view/helpers/tags/text_field.rb
index c579e9e79f..fe9c1a8d49 100644
--- a/actionview/lib/action_view/helpers/tags/text_field.rb
+++ b/actionview/lib/action_view/helpers/tags/text_field.rb
@@ -14,6 +14,7 @@ def render
options["type"] ||= field_type
options["value"] = options.fetch("value") { value_before_type_cast } unless field_type == "file"
add_default_name_and_id(options)
+ options.delete("multiple")
tag("input", options)
end
<%= form_with url: "/", method: :get, scope: :artists_grid do |f| %>
<%= f.text_field :birth_year, multiple: true %>
#=> <input type="text" name="artists_grid[birth_year][]" id="artists_grid_birth_year">
<% end %>