What is the proper way to add a custom input field to a LiveView form in Elixir that can use a decimal mask or credit card format? Ideally, this would behave like a standard input field, without requiring extra handlers on the Elixir side to process the information, other than the usual form validation handler.
I have discovered that Phoenix has a method called pushInput
to submit forms. It would be wonderful if we could easily add this functionality to enhance our forms with reusable components, leading to an improved user experience.
3 Likes
@chrismccord do you have any suggestion how can I achieve this?
The js interop guide has a controlled input example to do exactly this 
For example, the markup for a controlled input for phone-number formatting could be written
like this:
<input type="text" name="user[phone_number]" id="user-phone-number" phx-hook="PhoneNumber" />
Then a hook callback object could be defined and passed to the socket:
let Hooks = {}
Hooks.PhoneNumber = {
mounted() {
this.el.addEventListener("input", e => {
let match = this.el.value.replace(/\D/g, "").match(/^(\d{3})(\d{3})(\d{4})$/)
if(match) {
this.el.value = `${match[1]}-${match[2]}-${match[3]}`
}
})
}
}
let liveSocket = new LiveSocket("/live", Socket, {hooks: Hooks, ...})
https://hexdocs.pm/phoenix_live_view/js-interop.html#client-hooks-via-phx-hook
6 Likes
Thanks @chrismccord. That is was I look for
.
const formatNumber = (input) => {
const sanitizedInput = String(input).replace(/,|\./g, "").padStart(3, "0")
return sanitizedInput.replace(/^0*(\d+)(\d{2})$/, "$1.$2")
}
/**
* @type {import("phoenix_live_view").ViewHook}
*/
export const DecimalInput = {
/**
* Some times the server sends a float value with a single decimal place, like 1.0
* This function formats the value to 1.00 so the user can edit the value with 2 decimal places
*/
_formatSeverValue() {
if (this.el.value) {
const value = parseFloat(this.el.value.replace(/,/g, ""))
this.el.value = formatNumber(value.toFixed(2))
}
},
mounted() {
this._formatSeverValue()
this.el.addEventListener("input", (_event) => (this.el.value = formatNumber(this.el.value)))
},
updated() {
this._formatSeverValue()
},
}
Can I assume if I change a hidden input value using javascript, the value also will be propagated to the server?
Nope, you’ll want to manually trigger a phx-change
event as described below:
Triggering phx-
form events with JavaScript
Often it is desirable to trigger an event on a DOM element without explicit user interaction on the element. For example, a custom form element such as a date picker or custom select input which utilizes a hidden input element to store the selected state.
In these cases, the event functions on the DOM API can be used, for example to trigger a phx-change
event:
document.getElementById("my-select").dispatchEvent(
new Event("input", {bubbles: true})
)
When using a client hook, this.el
can be used to determine the element as outlined in the “Client hooks” documentation.
source: Form bindings — Phoenix LiveView v0.19.3
2 Likes