Allow function definitions on liveview `attr`

Hi everyone.

On this github issue Provide a unified mechanism to message the parent/child · Issue #1597 · phoenixframework/phoenix_live_view · GitHub, @chrismccord suggests that we can use “callback” attributes for a live view component to handle messages that needs to be sent to a parent.

For example, let’s say we have a live component that needs to send a message back to a parent live component/liveview, something like this:

defmodule MyLiveComponent do
  use CoreWeb, :live_component

  attr :id, :string, required: true
  attr :on_complete, :any, required: true

  def live_render(assigns), do: ~H"<.live_component module={__MODULE__} {assigns} />"

  def update(assigns, socket), do: ...

  def handle_event("some_event", _, socket) do
    %{on_complete: on_complete} = socket.assigns
    on_complete.(some_value)
    {:noreply, socket}
  end

  def render(assigns), do: ...
end

With this, I can now instantiate this component like this:

<MyLiveComponent.live_render
  id="id"
  on_complete={fn value -> send(self(), {:value, value}) end}
/>

Or like this:

<MyLiveComponent.live_render
  id="id"
  on_complete={fn value -> send_update(__MODULE__, id: @id, action: :value, result: value) end}
/>

This works great and make the component very flexible. But it at the same time it is not very clear for the end user what exactly :on_complete takes since there is no way to define a function definition for it right now.

For a small component this is fine since I can just check the handle_event call and understand what the function takes, but if my component is more complex, or if I’m making a component for a library, that can be a big issue.

I thought about creating an issue in liveview’s github about this, but first I guess it would make more sense to discuss with the community if everyone thinks this would be a good idea or if there is a good workaround.

Also, it would be great to get some suggestion on how a function definition would work in this case.

An obvious one would be to have something like this:

attr :on_complete, {:function, 3}
attr :on_complete, {:function, [:string, :integer, :any]}
attr :on_complete, {:function, [arg_name_1: :string, arg_name_2: :integer, arg_name_3: :any]}

Any suggestions are welcome.

I did some digging in LV’s code and created a PR that adds support for these new attr types: