Phx-change not fired inside LiveView

I converted a LiveComponent to a LiveView in order to support an async Task. When my code was in a LiveComponent, my phx-change fired for my input, and my phx-click fired for my button. Now that the code’s inside a LiveView, I deleted the phx-target={@myself} from both fields and the input event has gone silent. I still get the phx-click from the button, but don’t even see any messages about unhandled events for the input. Why does input work in LiveComponent but not LiveView?

def render(assigns) do
    <input value={@query} name="query" phx-change="change_query" />
    <button phx-click="search" type="button">Search</button>

def handle_event("change_query", %{"query" => query}, socket) do
  {:noreply, assign(socket, query: query)}

def handle_event("search", %{}, socket) do
  {:noreply, assign(socket, loading: true)}

is this inside a form? phx-change events are only emitted when inside a form AFAIK.

1 Like

Yes, it’s inside a form. Though that form is higher up in the hierarchy so maybe that’s messing things up?

|- LiveComponent with form
   |- LiveComponent
      |- LiveView w/ problemantic input

This same hierarchy worked when that last LiveView was a LiveComponent

Mmh… that looks like quite a complex hierarchy of views and components. I haven’t checked, but I suspect that a form spread across 2 LiveViews (and thus 2 processes) might cause problems. I would probably try to simplify and make sure you’re dealing with only 1 LiveView.

I’d love to just have one LiveView, but sadly handle_info isn’t supported by LiveComponent. So to have a self-contained component that can do async work, I had to convert it to a LiveView. It’s definitely ugly since LiveComponent and LiveView are declared and pass values differently, but I’m not sure what else to do.

An other option is to use tasks and send_update for asynchronous work in live components.


Oh wow- yeah that worked. I’m not sure why I was finding much more complex solutions. The last part that tripped me up is I had to follow the docs precisely and call self() outside of my task. Putting it inside the task returned some other value that didn’t track back to my component:

def handle_event("search", %{"query" => query}, socket) do
  pid = self()

  Task.start(fn ->
    {:ok, results} =
    send_update(pid, __MODULE__, id:, search_results: results)

  {:noreply, assign(socket, loading: true)}

Thanks everyone!

1 Like