kipcole9 / money_sql

Money functions for the serialization of a money data type in Elixir
Other
28 stars 18 forks source link

Is it able to change the behavior of to_string for `Money.Ecto.Composite.Type` ? #23

Closed Daniel-Xu closed 2 years ago

Daniel-Xu commented 2 years ago

Right now, if we use en as default locale, the to_string return $132 for currency. However, this is not recognized by DOM field. Is there an option to change this behavior?

image

kipcole9 commented 2 years ago

Daniel, thanks for the report. Can you send me the HTML of the relevant part? What are you trying to achieve? What output are you looking for?

kipcole9 commented 2 years ago

BTW, the actual number formatting is done by Cldr.Number.to_string/3 so you can check the options available there too.

Daniel-Xu commented 2 years ago

I'm using live view and live validation, the html is normal number_input

                <%= label f, :single_price, "", class: "uppercase md:text-sm text-xs text-gray-500 text-light font-semibold" %>
                <%= number_input f, :single_price, autocomplete: "off">

from the changeset, the single_price is type Money, this will be serialized into $333.00. However, the dom differ can't parse this format.

#Ecto.Changeset<
  action: :validate,
  changes: %{

    single_price: Money.new(:USD, "333"),

  }
Daniel-Xu commented 2 years ago

From the error in console, I'm guessing the to_string result of Money.Ecto.Composite.Type can't work well with live_view dom diff.

What I want to achieve is to be able to change the output of to_string for Money.Ecto.Composite.Type to 333 instead of $333. Hope it's clear.

Daniel-Xu commented 2 years ago

I was able to solve this by:

  1. change number_input into text_input
  2. using currency_symbol: "" (this is not necessary, but it makes the field look like a number field)
kipcole9 commented 2 years ago

Daniel, the error is probably because you are using a number input field and providing it with a currency string that includes non-digit input.

This has nothing to do with Money.Ecto.Composite.Type. The only relevant type is Money.t which combines a currency code and a decimal amount.

I believe you have a couple of choices:

  1. Don't use a number input type, use a text input type. And then use Money.parse/2 in your controller to parse it back into a Money.t. This allows you quite a flexible input mechanism but it does mean you don't get any benefit from browser-side number formatting and validation. You do get a "round trip" experience though in which the output string can be parsed back into a Money.t if you need.

  2. Just format the currency amount as a number and pass the number into the DOM. You can get the amount with Money.to_decimal/1 and then format it any way you want (Cldr.Number.to_string/3 or Decimal.to_string/2) for example.

The highest priority for ex_money is to preserve the integrity of a money-with-currency type and therefore I have no plan to modify Money.to_string/2 from its current behaviour.

kipcole9 commented 2 years ago

Daniel, glad you found a good way forward. I know that there needs to be a better way to format and enter currency amounts in HTML and its on my "to do" list. Thanks for letting me know what you did so I can share that if asked again.