zoedsoupe

zoedsoupe

While searching and studying about live view I found this page of LiveComponent documentation, and reading more carefully about the “cost of a live component”, here: Phoenix.LiveComponent — Phoenix LiveView v1.2.5

Then it gives a not to do example, using a LiveComponent to wrap DOM events, like:

defmodule MyButton do
  use Phoenix.LiveComponent

  def render(assigns) do
    ~H"""
    <button class="css-framework-class" phx-click="click">
      <%= @text %>
    </button>
    """
  end

  def handle_event("click", _, socket) do
    _ = socket.assigns.on_click.()
    {:noreply, socket}
  end
end

So it preferable to use a functional component! Then, it was I did! I have this function component to wrap and style the native DOM button tag:

  attr :style, :string, values: ~w(primary secondary), required: true
  attr :submit, :boolean, default: false
  attr :icon, :atom, required: false, default: nil
  attr :class, :string, default: ""
  attr :rest, :global, doc: ~s(used for phoenix events like "phx-click" and "phx-target")

  slot :inner_block

  def button(assigns) do
    ~H"""
    <button
      type={if @submit, do: "submit", else: "button"}
      class={["btn", "btn-#{@style}", @class]}
      {@rest}
    >
      <.icon :if={@icon} name={@icon} />

      <.text :if={@style == "primary"} size="base" color="text-white-100">
        <%= render_slot(@inner_block) %>
      </.text>

      <.text :if={@style != "primary"} size="base" color="text-blue-80">
        <%= render_slot(@inner_block) %>
      </.text>
    </button>
    """
  end

To be reusable in any phoenix template with a predefined style. My question is, how this function component can handle events, in this case, phx-click?

I used the button like this: https://github.com/peapescarte/pescarte-plataforma/blob/main/lib/pescarte_web/templates/landing_html/show.html.heex#L56-L58

However no event is trigged as can be seen in this little video:

Showing Posts 1 to 10

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

In short, it can’t. In order for a component to receive events it needs to be a stateful component that does use Phoenix.LiveComponent in its own module.

You generally also need to phx-target={@myself} to ensure that the events go to your component.

cmo

cmo

Function components do not handle events. Handle them in the parent liveview or livecomponent.

On a side note, this could be shorter:

<.text :if={@style == "primary"} size="base" color="text-white-100">
   <%= render_slot(@inner_block) %>
</.text>

<.text :if={@style != "primary"} size="base" color="text-blue-80">
  <%= render_slot(@inner_block) %>
</.text>

Could be:

<.text size="base" color={if @style == "primary", do: "text-white-100", else: "text-blue-80">
   <%= render_slot(@inner_block) %>
</.text>
zoedsoupe

zoedsoupe OP

so the template needs to be a live view and not a raw html template, right? Ok, this explain a lot! I’ll try to do this!

Oh, thanks for this little tip, I implement so fast I didn’t noticed haha. Actually I really would like to se a :else and :if-else directive like vue in the future, although it’s not mandatory

zoedsoupe

zoedsoupe OP

HEEx templates can be both live view or not, depending the usage of the screen! Ok, I need to practice more this concept

zoedsoupe

zoedsoupe OP

but phx-target only works inside a live view template, right? then for a button component phx-target to @myself doesn’t make much sense as it need to handle dozens of different events in different screens, not related.

I now see the problem is I’m trying to handle an event on a “raw” html (heex) template and not a live view

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

I don’t really understand what this means. A functional component used by a root live view is still “in live view”. If you have a phx-click on a functional component then the event will simply go to the live view itself. There is no such thing as having a handle_event without live view at all, what would even be handling the event?

zoedsoupe

zoedsoupe OP

I mean: for a new phoenix project we have:

./lib/pescarte_web/
├── controllers
│   ├── app
│   │   └── researcher_controller.ex
│   ├── landing_controller.ex
│   └── login_controller.ex
├── layouts
│   ├── app.html.heex
│   └── root.html.heex
├── layouts.ex
└── templates
    ├── app
    │   ├── profile_html.ex
    │   └── researcher_html
    ├── error_html
    │   └── 404.html.heex
    ├── error_html.ex
    ├── landing_html
    │   └── show.html.heex
    ├── landing_html.ex
    └── login_html.ex

simplified. Then, those landing_html.ex files aren’t live views, are templates that can use components, but they do not handle events, right?

Example, here’s my login_html.ex file:

defmodule PescarteWeb.LoginHTML do
  use PescarteWeb, :html

  def show(assigns) do
    assigns = Map.put(assigns, :form, to_form(%{}, as: :user))

    ~H"""
    <main class="fish-bg h-full">
      <.simple_form for={@form} action={~p"/acessar"} class="login-form">
        <.text size="h3" color="text-black-80">
          Faça login para acessar a plataforma
        </.text>

        <fieldset class="login-fieldset">
          <.text_input field={@form[:cpf]} type="text" mask="999.999.999-99" label="CPF" required />
        </fieldset>

        <fieldset class="login-fieldset">
          <.text_input field={@form[:password]} type="password" label="Senha" required />
        </fieldset>

        <:actions>
          <div class="flex justify-between items-center">
            <.checkbox field={@form[:remember_me]} label="Mantanha-me conectado" id="remember" />

            <DesignSystem.link href={~p"/usuarios/recuperar_senha"} class="text-sm font-semibold">
              <.text size="sm">Esqueceu sua senha?</.text>
            </DesignSystem.link>
          </div>
        </:actions>

        <:actions>
          <.button style="primary" submit>
            <.text size="lg">Acessar</.text>
          </.button>
        </:actions>
      </.simple_form>
    </main>
    """
  end
end

This is not a live view, so cannot handle event and therefore does not define @myself. Am I wrong? To define a live view I need to use Phoenix.LiveView then implement mount/3 and handle_event/3

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Hey @zoedsoupe you’re absolutely right, it’s only very recently that Phoenix started using heex for controllers and I basically totally forgot it does that now!

So back to your question: Yea you can only do handle_event and phx-target stuff on components used in a live view. If you do need to use a functional component between both live and traditional views you could probably pass in myself: nil as an assign, but also the button won’t really work.

zoedsoupe

zoedsoupe OP

Thanks for confirming this conception! there’s any part of the official documentation that explain this difference explicitly? I think the most important part is to understand that function components will not work with events on controllers templates.

For now i don’t need a component to trigger events on controller templates, a form action here is to save the session, so it can be safely a submit button to another route.

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Not in the way you’re looking for that I see. That said if you read Components and HEEx — Phoenix v1.8.8 and the other docs it links to, you’ll notice that the word event never even appears. It’s only when you get to the live view docs do you start seeing events and handle_event.

— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
ChrisAmelia
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication): toke...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews