LiveView phx-change attribute does not emit event on input text

Hi everyone,

I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the text change. So I added phx-change attribute on every input (I only have text, email and password field) but it does not emit any event when the text of any of my input field change. Strangely It emits an event when instead of phx-change I use phx-click. Am I adding the wrong attribute?
On React I used to add onChange={myCallback} attribute on text input, is there something similar? I checked phoenix_live_view.js doc but can’t find a solution. Could someone help me?

1 Like

Today we require the phx-change to be on the parent form, not individual inputs

4 Likes

Thank you for the reply. Is this a design choice or it will probably be added in a near future?

At the moment I’m not planning on making it per input because you can get what you want with the form events, and every event will have all the form input so it’s generally a better approach.

1 Like

Doesn’t that mean you can’t quite as easily have composible form elements? Instead you’ll need to compose everything manually in code for each given form and all possible fields that it might dynamically have. In Drab I just use a drab child commander to add those and it is all very compartmentalized, so this would be a very useful feature in liveview.

4 Likes

I need to see concrete use cases first before settling on a broader API. Having the top-level form control its input is preferred because it’s generally what you want event wise, where a single change event provides the current client form state. More critically, it also allows us to more easily control the loading/disabled/patch states of form events where ad-hoc components make this more difficult, and open up race conditions to a programmer concern instead of library concern. For example, LV can lock the form or defer patches to the form before a server acknowledgement from the pushed form event, where ad-hoc inputs would not. So my current preference is to nail down the loading state and deferred patch handling at the form level and see if we need to go further from that base.

My initial thought is an autocomplete input, I want the input code to handle that, not the form itself as it doesn’t care, and adding that to every form’s handler just in the off chance the autocomplete version is used in the HTML is extra mental bandwidth to handle, plus however many other plugins the overall system will include over time. The autocomplete input is the precise use that I use a drab submodule for. In HTML all I have to do is craft an html element with special drab-* attributes and it auto-registers and hooks in the child commander, which uses the information in the attributes to know which autocomplete list to pull from (which can even push updates in real time without typing changes if the autocomplete list has backend changes). :slight_smile:

10 Likes

Why can’t you just use phx-keyup for individual form elements? That’s what I’m using and it works great.

1 Like

You can, but you still have to tie it into the backend somehow, the frontend is not the issue. ^.^

1 Like

phx-keyup will pass the value of the input to the handle_event function which you can use to return the autocomplete options. Is that what you are trying to do?

1 Like

Not me, I’m referencing putting in pre-built blocks into a page. For example, drab allows you to do something like <div drab-commander="MyCommander">...</div> and the div and everything in it is controlled by an entirely different commander (like a liveview socket) rather than the main one, allowing you to break up functionality and re-use it in a trivial manner.

Though perhaps you meant to reply to the OP rather than my post? ^.^;

2 Likes

Oh!

I am trying to do exactly this in the future. But couldn’t you have a LiveView template conditionally render another template inside of it? Haven’t tried this yet but will be crossing this bridge soon.

You can conditionally render all you want as it is right now, but the backend functionality has to support everything you potentially want to do, whether actually using it or not, instead of allowing the system itself to manage it. It’s the main feature I’m waiting for liveview as my server design is integrally tied into the drab subcommander style of piecing things together (which I adore as it works really really well!), plus some other features like more fine-grained control of the page that liveview does not allow for yet without some really convoluted work-arounds that don’t fit even remotely easy, but is trivial in drab. ^.^;

2 Likes

This is a massive bummer. I have a really, really big form with one country select in the middle. Based on the selected country, I want to display something differently. I do absolutely NOT want a phx-change on my form (massive amount of data on the wire (textareas with tens of thousands of words)), I just want a phx-submit. How do I detect the changes to just my country select? Doesn’t seem like I can nest forms either.

5 Likes

Hi @ryan-senn,

first of all, welcome to the Elixir Forum! I do have the same issue – I want finer-grained control. Right now, I abuse the phx-keyup in conjunction with phx-focus and phx-value to achive the desired function:

phx-focus will send the currently focused input element identified by the value in phx-value /some index). The value of the input field itself will be send on each keyup event.

See https://github.com/gutschilla/phoenix-liveview-todo-app/blob/master/lib/live_demo_web/views/page_view.ex for an example.

Thanks for your answer. I’ve read about a few other people (ab)using keyups to suit their needs.

The input that I want to listen to is a select and not a text input however (I assume no keyup?). I haven’t actually tried to dig deeper just yet, the app we’re developing won’t be released for another 2 months, making it a not so urgent situation.
I mainly wanted to share my use case to demonstrate that input level changes can be beneficial in some circumstances. I just intuitively assumed that it was an option, so it was quite a surprise to find out that it isn’t. Maybe because of previous experience with elm/react etc.

Thanks again for your example, it’s an interesting trick. I’m sure it will help out quite a few people searching how to do this!

2 Likes

Sorry to resurect old topic but I have question about how the combination of keyup, focus and value work.

I still don’t understand which event does what?
I’ve learned that phx-change cannot overwrite the value in a focused text input. Is the phx-focus somehow mitigating that?

I think this is my biggest current wish for LiveView as well in terms of supporting composable forms.

With the awesome new live_component setup, it has become much easier to compose LiveViews. But, this feels hobbled by the current JS interop that requires handling forms as a single unit (without going all JS hooky). Some kind of override allowing one to manipulate input values outside of phx_submit would be especially helpful for me.

1 Like

Agree… I love the new live_components. I am using them extensively!

You can manipulate input values of any non-focused fields without using hooks by using phx-change. By default your handle_event will fire on every keystroke but you can use phx-debounce to have it fire after x ms or onBlur. In your handler you can feed the params into your form’s changeset function (which has all the validations), put_change any manipulations to non-focused fields, then generate a new form struct running form_for on the changeset. Then you just put the new form struct into your assigns and you should be good to go!

Mutating focused fields (like masked inputs and the like) I’m pretty sure you do need to use hooks probably due to latency and race conditions. You can do it with vanilla JS as shown in the docs but I like to use phoenix_live_react.

3 Likes

Hi,

what solution did you come up with?
I am on the same question… using select so cannot use keyup event.

better scenario…
how to choose state then choose city then region.
select box to choose state then show options for list of available cities.

list of states and list of cities will not be a part of changeset. but selected_city_id and selected_region_id will be.

# My LiveComponent

  def render(assigns) do

    ~L"""
    <div class="input-box">
    <%= select(@opts.form_handle, :select_id, @opts.city_pairs, phx_window_keydown: "city_selected") %>
    <span class="error-msg">
    <%#= error_tag @opts.form_handle, @opts.select_id %>
    </span>
    </div>
    """
  end

  def handle_event(msg, params, socket) do
    IO.puts "city selected .........................."
    {:noreply, socket}
  end

  def handle_event("city_selected", %{"landmark" => landmark_params}, socket) do
    IO.puts "city selected .........................."
    # IO.puts inspect(params)
    {:noreply, socket}
  end

# it straight goes to form event...