Hello,
I noticed that in some cases the input value will change/be updated after a phx-change
call from a form but its hook code will never be called.
For example, consider this hook:
import Imask from "imask"
const PriceUSDMaskHook = {
mask: null,
mounted() {
this.mask = IMask(this.el, {
mask: Number,
scale: 2,
signed: false,
thousandsSeparator: ",",
radix: ".",
})
},
beforeUpdate() {
console.log(">>>>> beforeUpdate", this.el.value)
},
updated() {
console.log(">>>>> updated begin", this.el.value)
this.mask.value = this.el.value
console.log(">>>>> updated end", this.el.value)
},
destroyed() {},
disconnected() {},
reconnected() {}
}
export default PriceUSDMaskHook
Now, let’s say I have this form:
<.form :let={f} for={@blibs} phx-change="validate">
<label>element 1</label>
<.text_input form={f} field={:price} phx-hook="PriceUSDMaskHook" />
<label>element 2</label>
<.text_input form={f} field={:blobs} />
</.form>
And I have this validation code:
def handle_event("validate", params, socket) do
params = maybe_process_price(params)
{:noreply, assign(socket, blibs: params)}
end
defp maybe_process_price(%{"price" => ""} = params), do: params
defp maybe_process_price(%{"price" => price} = params) when is_binary(price) do
price = price |> Money.parse!(:USD) |> Money.to_string(symbol: false, separator: "")
%{params | "price" => price}
end
As you can see, my hook will reformat the number the user typed into a nicer format, for example, 23423
will be changed to 23,423
.
When I send that value to the validate function, I need to convert it back to a decimal since my schema accepts a price as decimal, I removed the schema part and the actual validation since that is not relevant here, but in the real code I also run that function to convert the price string into a decimal and I replace that value in the form before doing validation.
As a side-effect of that, the returned form will container the decimal, not the original string, that’s why I have the updated
callback implemented in my hook since that callback will reapply the mask so the final number is, again, 23,423
.
This works fine if I’m changing that input element, but it fails if I change any other input element inside the form (like the element 2
in this example).
What will happen in this case is that the new form returned will have that value changed, but LiveView will not detect that and will never call the updated
callback, so the value 23,423
will be replaced with 23423
.
I believe this is a bug, but I wanted to check here in the forum first before creating a ticket in github.