htmlruby-on-railsdatagridw3c-validation

HTML error of multiple attribute for input type="text" with Rails datagrid gem


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

  1. Is this error ("Attribute multiple not allowed on element input with type="text") applied for HTML 5 only or something? Was it actually allowed in previous HTMLs? (Just my guess)
  2. Do most major browsers interpret input type="text" multiple="multiple" fields as multiple fields and return an Array?
    • If not, is there any valid way to specify a pair (or list) of text fields with a common label in a HTML form?
  3. Is there any way to circumvent the issue to force datagrid to produce a HTML5-valid HTML?

Specifics for the above-mentioned example

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

enter image description here

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.

Update (solved fundamentally in November 2024)

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.


Solution

  • 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 %>