I'm getting `no function clause matching in DerpyToolsWeb.UtmBuilderLive.handle_event/3`

I have created a function component, <.inspector />, that fires an event. inspect-source.

defmodule DerpyToolsWeb.DevComponents do
  @moduledoc """
  Useful component for development.
  Can be embedded into other components, which will allow developers to open
  the source code of the component directly from browser.
  """
  use Phoenix.Component

  def inspector(assigns) do
    ~H"""
    <div class="absolute -top-10 flex justify-end w-full">
      <%!-- To link directly to the storybook page! --%>
      <button class="rounded-tl-lg rounded-bl-lg p-2 bg-slate-100 m-0" title="Show in Catalog">
        <Heroicons.eye solid class="h-3 w-3 text-gray-500" />
      </button>
      <button
        phx-click="inspect-source"
        phx-value-file={@file}
        phx-value-line={@line}
        class="-ml-1 rounded-tr-lg rounded-br-lg p-2 bg-slate-100 m-0 border-l border-slate-200"
        title="Open in VS Code"
      >
        <Heroicons.code_bracket solid class="h-3 w-3 text-gray-500" />
      </button>
    </div>
    """
  end
end

Add the component to the html_helpers function, so we won’t have to import it again and again:

defp html_helpers do
    quote do
      ...
      # Custom Dev Components
      import DerpyToolsWeb.DevComponents
      ...
    end
end

Then place the inspector component anywhere and when we click on inspect source button, the file opens up appropriately in the code editor.

<div class="w-60 m-auto relative">
   <.inspector :if={Mix.env() == :dev} file={__ENV__.file} line={__ENV__.line} />
</div>

Instead of writing the handle_event function in every component, where I wish to add the <.inspector />, I just created a module to hold the common handle_event callbacks.

scope "/", DerpyToolsWeb do
    pipe_through([:browser])

    live_session :no_log_in_required,
      on_mount: [DerpyToolsWeb.Nav] do
        live("/utm-builder", UtmBuilderLive)
        live("/metadata-analyzer", MetadataAnalyzerLive)
    end
end
defmodule DerpyToolsWeb.Nav do
  import Phoenix.LiveView

  def on_mount(:default, _params, _session, socket) do
    {:cont,
     socket
     |> attach_hook(:inspect_source, :handle_event, &handle_event/3)}
  end

  def handle_event("inspect-source", %{"file" => file, "line" => line}, socket) do
    System.cmd("code", ["--goto", "#{file}:#{line}"])

    {:cont, socket}
  end

  def handle_event(_, _, socket), do: {:cont, socket}
end

In that module, I have defined the handle_event function and it works as well.

In the router, I just specified that module using on_mount.

However, I still see this error, even though I have declared a common handle_event!
no function clause matching in DerpyToolsWeb.UtmBuilderLive.handle_event/3.


Here’s how it works:
Inspector In Action


I have added this suggestion to Live View, hopefully, it gets added to the later version of Live View!


N.B. You have to add this to your environment so that the BEAM will open the correct editor for you and jump to the right file and line!

export ELIXIR_EDITOR="code --goto __FILE__:__LINE__"


P.S. I know I can do this:

def handle_event(_, _, socket), do: {:noreply, socket}
or

def handle_event(event, params, socket) do
    IO.inspect(event)
    IO.inspect(params)
    {:noreply, socket}
end

But, I prefer not to.

Your hook is returning {:cont, socket}, meaning that the lifecycle stage (:handle_event in this case) will continue and LiveView will look for a matching handle_event clause in your UtmBuilderLive module, which apparently doesn’t exist. Change the hook to return {:halt, socket} to signal that you’re done handling the event.

2 Likes

You are a lifesaver.

Thank you for the insight.

I just assumed that returning halt would prevent me from adding more events/hooks to the pipeline. (i.e. Rest of the steps in the pipeline would be skipped since I return halt.)

3 Likes