<.input type="date" /> in Phoenix 1.7.12 simple form

I am learning elixir with the phoenix framework version 1.7. and it comes with the core_components.

inside you will find

# All other inputs text, datetime-local, url, password, etc. are handled here...
 def input(assigns) do
   ~H"""
   <div phx-feedback-for={@name}>
     <.label for={@id}><%= @label %></.label>
     <input
       type={@type}
       name={@name}
       id={@id}
       value={Phoenix.HTML.Form.normalize_value(@type, @value)}
       class={[
         "mt-2 block w-full rounded-lg text-neutral-50 bg-neutral-800 focus:ring-0 sm: sm:leading-6 border-2",
         "phx-no-feedback:border-008B8B phx-no-feedback:focus:border-amber-600",
         @errors == [] && "border-008B8B focus:border-amber-600",
         @errors != [] && "border-rose-400 focus:border-rose-400"
       ]}
       {@rest}
     />
     <.error :for={msg <- @errors}><%= msg %></.error>
   </div>
   """
 end

However i noticed a small bug. if you say type=“date” and directly pass in the database value of :utc_datetime the field stays empty.

I tried to resolve this by adding my own input type

  def input(%{type: "date"} = assigns) do

    ~H"""
    <div phx-feedback-for={@name}>
      <.label for={@id}><%= @label %></.label>
      <input
        type={@type}
        name={@name}
        id={@id}
        value={Phoenix.HTML.Form.normalize_value(@type, if @value do Date.to_iso8601(@value) else @value end)}
        class={[
          "mt-2 block w-full rounded-lg text-neutral-50 bg-neutral-800 focus:ring-0 sm: sm:leading-6 border-2",
          "phx-no-feedback:border-008B8B phx-no-feedback:focus:border-amber-600",
          @errors == [] && "border-008B8B focus:border-amber-600",
          @errors != [] && "border-rose-400 focus:border-rose-400"
        ]}
        {@rest}
      />
      <.error :for={msg <- @errors}><%= msg %></.error>
    </div>
    """
  end

this works almost like expected as in i can send a date using type=“date” as long as i do this in the controller

 def create(conn, %{"task" => task_params}) do

    #modify the user input slighly to morph it into the database schema better.
    task_params = Map.put(task_params, "creator_id", conn.assigns.current_user.id)

   task_params if task_params["due_date"] do
      corrected_date = task_params["due_date"] <> " 00:00"
      Map.put(task_params, "due_date", corrected_date)
    else
      task_params
    end
#proceed to save task without issue

however the issue that i encounter now is when i start adding validation. For that i am using a <.simple_form> with an ecto changeset.

When anything is invalid i get
“no function clause matching in Date.to_iso8601/2”

the value it gets back is of string “2024-01-01” after trying debug this for a while, but from the database i get back ~U[2024-06-25 00:00:00Z].

The value ~U[2024-06-25 00:00:00Z] seems to work just fine. but “2024-01-01” does not.

Without the Date.to_iso8601(@value) part the date ~U[2024-06-25 00:00:00Z] will not pass correctly to Phoenix.HTML.Form.normalize_value(“date”, value)

i have tried to write functions that parse dates and strings but i cannot seem to know the difference between the 2 inputs.

I assume i am doing something simply wrong in my approach and welcome any insight in my troubles.

Thanks in advance!

The fact that you can have both elixir terms as well as strings returned is expected behaviour due to what I described here:

Is there a reason to use a datetime field if you just deal with dates though? This should work just fine if the schema would use a date field like the form does.

You are 100% correct.

I come from a c# background not knowing that in elixir there is a difference between DateTime and Dates.

After reading your suggestion of just using :date everything just worked straight away.

I was trying to make it possible to use datetime and dates when they are fundamentally different.

Thank you for your reply!