Phx-change on input themselves

Hello everybody,

I have a form with many inputs…
One of those inputs (for example a date or a quantity) affects for example a total price. I wanted to display that estimated price only when the inputs related to the price are changing.

Using phx-change at the form level will trigger so many events because of all the other inputs (I don’t even think for textarea being typed) and I don’t want that. And using debounce will make me lose the reactivity I want for the only fields related to the total.

Using phx-blur on those input works, but the user needs to leave those input fields which may not occurs immediately particularly when using for example the built-ins arrows for numeric input fields, or when manipulating the built-ins calendars.

Am I the only one who don’t understand why phx-change doesn’t apply for inputs themselves only?

If I should bring some JS in order to do that, I lose the main purpose of why I want to use Live View…

Anyway, is there some kind of workaround I can do?

I thought of making many little forms (at least for the fields participating to the total), but I’m using a changeset.
Currently, when I’m using phx-blur, I’m updating the changeset in which there is both the total and the related quantity or date fields. So upon the render, I have nothing to do.
So I want to keep the whole changeset based form.

I even tried to nest the inputs field within a bare <form phx-change> ... </form>
But indeed it’s not correct HTML, and the form doesn’t even display in the source…

4 Likes

You can pattern match based on the input that triggered the phx-change action.

Example from the docs:

For example, if the following input triggered a change event:

<input name="user[username]"/>

The server’s handle_event/3 would receive a payload:

%{"_target" => ["user", "username"], "user" => %{"username" => "Name"}}

Then you can have the following callbacks defined:

def handle_event("phx_change_action", %{"_target" => ["user", "username"], "user" => %{"username" => username}}, socket) do
... handle logic when username is changed
end
def handle_event("phx_change_action", %{"_target" => ["user", "quantity"], "user" => %{"quantity" => quantity}}, socket) do
... handle logic when quantity is changed
end

…and so on…

You can achieve a per input change like this. The idea is that you are not forced into only one handle_event callback. You can achieve the level of granularity that you wish by simply pattern matching on the payload.

3 Likes

Thank you for your input,

In fact I did exactly that so the problem was not really to be able to find the correct input I’m interested in.
But rather the fact that every keystroke on all the other inputs (I’m not interested in) triggers a WS roundtrip (or at least a one way trip).
I’m testing locally (so without really any network latency) and after a couple of fields, the keystrokes or not smooth anymore at all. I mean the text inputs lag a lot. Which is not acceptable.

Using debounce can help, but I’m don’t want to have a debouncing timeout for the couple of fields I’m interested in. It will make the purpose of live-updating the live-calculation useless.

In the meantime, I’ll try with some JS to see if I can end-up with something that works well…

The debounce is set per input. For the inputs that you want to ignore for the phx-change action, set it to blur and have the handle_event callback pattern match those (or pattern match those you’re interested in instead and have a catch-all that ignores everything else) and simply return the unmodified socket in that case.

1 Like

I forgot (or even didn’t really notice) the blur option.
Indeed, this way, I was able to get the behavior I want…

Thanks for the insight…

Now I can ditch all that spaghetti JS I wrote :sweat_smile: