Live_component event going to Live_view parent instead of itself

Please Help

I have an issue where all my event to a Live Component are passed to the Live View parent instead …

The Live Component …

defmodule EzsysWeb.CounterLive do
  use Phoenix.LiveComponent

  @impl true
  def mount(socket) do
    IO.puts("I am in the counter Mount")
    {:ok, assign(socket, :count, 0)}
  end

  @impl true
  def render(assigns) do
    ~H"""
    <div class="items-center body_text">
      <button phx-click="decrement">-</button>
      <span>Count: <%= @count %></span>
      <button phx-click="increment">+</button>
    </div>
    """
  end

  @impl true
  def handle_event("increment", _, socket) do
    {:noreply, assign(socket, :count, socket.assigns.count + 1)}
  end

  @impl true
  def handle_event("decrement", _, socket) do
    {:noreply, assign(socket, :count, socket.assigns.count - 1)}
  end
end

and the live-view that calls the above is …

defmodule EzsysWeb.TestLive do
  use EzsysWeb, :live_view

  def mount(_params, _session, socket) do
    IO.puts("I am in mount of EzsysWeb.LoanLive")
    {:ok, socket}
  end

  def render(assigns) do
    ~H"""
    <.live_component id="counter" module={EzsysWeb.CounterLive} />
    """
  end
end

The error I get is …


[error] GenServer #PID<0.3462.0> terminating
** (UndefinedFunctionError) function EzsysWeb.TestLive.handle_event/3 is undefined or private
    EzsysWeb.TestLive.handle_event("increment", %{"value" => ""}, #Phoenix.LiveView.Socket<id: "phx-F8EW-maMa-SGRhGk", endpoint: EzsysWeb.Endpoint, 
view: EzsysWeb.TestLive, parent_pid: nil, root_pid: #PID<0.3462.0>, router: EzsysWeb.Router, assigns: %{__changed__: %{}, current_user: #Ezsys.Accounts.User<__meta__: #Ecto.Schema.Metadata<:loaded, "users">, id: "358f8f85-e8df-4388-9d05-49e9f9867a89", email: "q@q.q", confirmed_at: nil, inserted_at: ~U[2024-03-05 18:50:57Z], updated_at: ~U[2024-03-05 18:50:57Z], ...>, flash: %{}, live_action: nil}, transport_pid: #PID<0.3454.0>, ...>)       
    (phoenix_live_view 0.20.2) lib/phoenix_live_view/channel.ex:487: anonymous fn/3 in Phoenix.LiveView.Channel.view_handle_event/3
    (telemetry 1.2.1) c:/Users/Gerry/Documents/source/phoenix/ezsys/deps/telemetry/src/telemetry.erl:321: :telemetry.span/3
    (phoenix_live_view 0.20.2) lib/phoenix_live_view/channel.ex:246: Phoenix.LiveView.Channel.handle_info/2
    (stdlib 5.1.1) gen_server.erl:1077: :gen_server.try_handle_info/3
    (stdlib 5.1.1) gen_server.erl:1165: :gen_server.handle_msg/6
    (stdlib 5.1.1) proc_lib.erl:251: :proc_lib.wake_up/3
Last message: %Phoenix.Socket.Message{topic: "lv:phx-F8EW-maMa-SGRhGk", event: "event", payload: %{"event" => "increment", "type" => "click", "value" => %{"value" => ""}}, ref: "9", join_ref: "4"}
State: %{socket: #Phoenix.LiveView.Socket<id: "phx-F8EW-maMa-SGRhGk", endpoint: EzsysWeb.Endpoint, view: EzsysWeb.TestLive, parent_pid: nil, root_pid: #PID<0.3462.0>, router: EzsysWeb.Router, assigns: %{__changed__: %{}, current_user: #Ezsys.Accounts.User<__meta__: #Ecto.Schema.Metadata<:loaded, "users">, id: "358f8f85-e8df-4388-9d05-49e9f9867a89", email: "q@q.q", confirmed_at: nil, inserted_at: ~U[2024-03-05 18:50:57Z], updated_at: ~U[2024-03-05 18:50:57Z], ...>, flash: %{}, live_action: nil}, transport_pid: #PID<0.3454.0>, ...>, components: {%{1 => {EzsysWeb.CounterLive, "counter", %{count: 0, id: "counter", __changed__: %{}, flash: %{}, myself: %Phoenix.LiveComponent.CID{cid: 1}}, %{live_temp: %{}, lifecycle: %Phoenix.LiveView.Lifecycle{after_render: [], handle_async: [], handle_event: [], handle_info: [], handle_params: [], mount: []}, root_view: EzsysWeb.TestLive}, {318875585207481669582375682519268286791, %{}}}}, %{EzsysWeb.CounterLive => %{"counter" => 1}}, 2}, topic: "lv:phx-F8EW-maMa-SGRhGk", serializer: Phoenix.Socket.V2.JSONSerializer, join_ref: "4", upload_names: %{}, upload_pids: %{}}

If I move the increment and decrement code from the component to the live-view parent along with creating a counter initialization to the mount of the TestLive live-view there is no error … it just does not increment and decrement the counter.

Try adding phx-target={@myself} to the buttons.

1 Like

This is the expected behavior. The parent Live View is the source of truth for state (and therefore, events that change the state).

As @absowoot mentioned, you have to target your Live Component’s handle_event/3 callback with @myself (which is the ID of this Live Component instance) so your button knows where the state is changed.

1 Like

Thanks @toberoni and @absowoot for answering so quickly. It works great.

1 Like