Adding class to date_select elements

You provided this as your reference case:

My purpose was to attempt to generate equivalent HTML output with a builder function.

So if we start out with (in iex):

import Phoenix.HTML
import Phoenix.HTML.Form

default_date = {2020, 12, 25}
content =
  ~E"""
    <%= date_select :user, :date, default: default_date %>
  """

safe_to_string(content)

We’ll get this output:

<select id=\"user_date_year\" name=\"user[date][year]\">
  <option value=\"2014\">2014</option>
  <option value=\"2015\">2015</option>
  <option value=\"2016\">2016</option>
  <option value=\"2017\">2017</option>
  <option value=\"2018\">2018</option>
  <option value=\"2019\">2019</option>
  <option value=\"2020\" selected>2020</option>
  <option value=\"2021\">2021</option>
  <option value=\"2022\">2022</option>
  <option value=\"2023\">2023</option>
  <option value=\"2024\">2024</option>
</select> /
<select id=\"user_date_month\" name=\"user[date][month]\">
  <option value=\"1\">January</option>
  <option value=\"2\">February</option>
  <option value=\"3\">March</option>
  <option value=\"4\">April</option>
  <option value=\"5\">May</option>
  <option value=\"6\">June</option>
  <option value=\"7\">July</option>
  <option value=\"8\">August</option>
  <option value=\"9\">September</option>
  <option value=\"10\">October</option>
  <option value=\"11\">November</option>
  <option value=\"12\" selected>December</option>
</select> /
<select id=\"user_date_day\" name=\"user[date][day]\">
  <option value=\"1\">01</option>
  <option value=\"2\">02</option>
  <option value=\"3\">03</option>
  <option value=\"4\">04</option>
  <option value=\"5\">05</option>
  <option value=\"6\">06</option>
  <option value=\"7\">07</option>
  <option value=\"8\">08</option>
  <option value=\"9\">09</option>
  <option value=\"10\">10</option>
  <option value=\"11\">11</option>
  <option value=\"12\">12</option>
  <option value=\"13\">13</option>
  <option value=\"14\">14</option>
  <option value=\"15\">15</option>
  <option value=\"16\">16</option>
  <option value=\"17\">17</option>
  <option value=\"18\">18</option>
  <option value=\"19\">19</option>
  <option value=\"20\">20</option>
  <option value=\"21\">21</option>
  <option value=\"22\">22</option>
  <option value=\"23\">23</option>
  <option value=\"24\">24</option>
  <option value=\"25\" selected>25</option>
  <option value=\"26\">26</option>
  <option value=\"27\">27</option>
  <option value=\"28\">28</option>
  <option value=\"29\">29</option>
  <option value=\"30\">30</option>
  <option value=\"31\">31</option>
</select>\n"

Note how the “2020”, “12” and “25” option elements have the selected attribute.

Now a builder version:

import Phoenix.HTML
import Phoenix.HTML.Form

make_builder = fn {year, month, day} ->
  fn b ->
    ~E"""
      <%= b.(:year, [value: year, options: 2014..2024, class: "def"]) %> / <%= b.(:month, [value: month]) %> / <%= b.(:day, [value: day]) %>
    """
  end
end

default_date = {2020, 12, 25}
builder = make_builder.(default_date)
content =
  ~E"""
    <%= date_select :user, :date, builder: builder %>
  """

safe_to_string(content)

which produces the same output.

EDIT:

Now having seen this code:

https://github.com/phoenixframework/phoenix_html/blob/v2.13.0/lib/phoenix_html/form.ex#L1391-L1395

This works as expected:

import Phoenix.HTML
import Phoenix.HTML.Form

builder =
  fn b ->
    ~E"""
      <%= b.(:year, [options: 2014..2024, class: "def"]) %> / <%= b.(:month, []) %> / <%= b.(:day, []) %>
    """
  end

default_date = {2020, 12, 25}
content =
  ~E"""
    <%= date_select :user, :date, builder: builder, default: default_date %>
  """

safe_to_string(content)

If you look at the date_select code it should become obvious that the :default value has already been extracted and is being handed to the builder function as the value to format.

By the time builder runs it’s too late to influence the :default because

value = Keyword.get(opts, :value, input_value(form, field) || Keyword.get(opts, :default))

has already happened.