How can I accept string on text_input allowing decimal, but have ecto modify it to integer for DB write?

I would like to allow user input to include the decimal to input monetary values (doing UI validation with cleave.js), but have Ecto change the value to an int to save to the database.

Currently the form shows the following error when I click the form save button, which I can’t locate in any of the Phoenix or Ecto libraries:

"Please enter a valid value. The two nearest valid values are .."

This occurs whether or not the text_input using the cleave js tool (ie, appears to not be related to the cleave library).

Based on this info, it appears the message is coming from the Ecto validators. Any ideas how I can change Ecto to allow string, then intercept to remove the decimal for Ecto to write the record to the db?

:wave:

Maybe you can create a custom ecto type that would convert decimals and floats to integers in dump or whatever callback is used to prepare the data for writing to db.

defmodule MyAlmostInteger do
  @behaviour Ecto.Type
  
  def type, do: :integer

  def cast(number) when is_number(number) do
    {:ok, number}
  end

  def cast(%Decimal{} = decimal) do
    {:ok, decimal}
  end

  def cast(_other), do: :error

  def load(int) when is_integer(int) do
    {:ok, int}
  end

  def dump(%Decimal{} = decimal) do
    {:ok, Decimal.to_integer(decimal)}
  end

  def dump(float) when is_float(float) do
    {:ok, Float.to_integer(float)}
  end

  def dump(int) when is_integer(int) do
    {:ok, int}
  end
  
  def dump(_other), do: :error
end

then in your schema

schema "some_table" do
  # ...
  field :some_column, MyAlmostInteger
  # ...
end
4 Likes