Create Elixir Ecto data from multiple form elements

261 Views Asked by At

This is specifically me creating a custom date from from elements. I have a form that collects the day and month of birth of a person (I'm not interested in the year). So I use the following, to create the form entry for the day_and_month_of_birth element this post is about:

<div class="form-group">
     <div class="row">
          <div class="col-md-5">Day / Month of Birth</div>
          <%= date_select f, :day_and_month_of_birth, builder: fn b -> %>
             <div class="col-md-3">
             <%= b.(:day, []) %>
             </div>
              /
             <div class="col-md-4">
             <%= b.(:month, []) %>
             </div>
          <% end %>
          <%= error_tag f, :day_and_month_of_birth %>
     </div>
</div>

In my model I have:

defmodule RocfDev.Registration do
   use RocfDev.Web, :model

schema "registration" do
  field :title, :string, virtual: true
  field :firstname, :string, virtual: true
  field :surname, :string, virtual: true
  field :day_and_month_of_birth, Ecto.Date, virtual: true
end

def changeset(model, params \\ %{}) do
   model
   |> cast(params, [
       :title, 
       :firstname, 
       :surname, 
       :day_and_month_of_birth
   ])
end

Currently when I post the form, I get the error unrecognized date %{"day" => "1", "month" => "1"}.

I suppose it's because there's no day and month field in the registration schema. My questions are:

1st, Since I'm not interested in the year of birth, is Ecto.Date still the appropriate type to use for the day_and_month_of_birth field?

2nd, How do I resolve the error?

1

There are 1 best solutions below

0
On

Basically, you have two integer ranges 1..12 and 1..31. But there are tons of limitations/constraints on their pair: 2 and 30 is invalid, etc. That’s why Ecto.Date type is still looking applicable here. To use it, one might force the year to always be, say, 2000, and pass this value back and forth to your frontend, using the hidden field to “display” it and pass back. That way you could not care about unrecognized date errors: from now on you’ll get %{"year" => 2000, "day" => "1", "month" => "1"} here.

On the other hand, just day and month value is an incomplete information to verify the date, due to leap years existence. You can’t prove whether February, 29 is a valid date of birth or not, without knowing the year. 2016/2/29 is valid, while 2017/2/29 is not. From this point of view, it sounds reasonable to quit abusing Ecto.Date and use just two range fields, both of type integer.