View Source LiveEvent behaviour (live_event v0.3.0)
Standardized event handling in LiveViews and LiveComponents.
the-problem
The problem
LiveView currently has two built-in mechanisms for sending messages between views and components:
send/2
->handle_info/2
for sending to a LiveViewsend_update/3
->update/2
for sending to a LiveComponent
Picking a singular approach limits component reusability, and using both approaches results in an inconsistent event API.
the-solution
The solution
LiveEvent standardizes these systems into a singular flow:
emit/3
->LiveEvent.handle_emit/4
for sending to a LiveView or LiveComponent
When use LiveEvent.LiveComponent
or use LiveEvent.LiveView
is invoked, it hooks into the lifecycle of the
view or component to transparently add support for the LiveEvent.handle_emit/4
callback.
event-destinations
Event destinations
Imagine a LiveComponent that has an :on_selected
event assign that is raised like so:
emit(socket, :on_selected)
To handle the event on a LiveView, pass a pid to the event assign.
<.live_component module={MyComponent} id="foo" on_selected={self()}>
To handle the event on a LiveComponent, pass {module, id}
to the event assign.
<.live_component module={MyComponent} id="foo" on_selected={{__MODULE__, @id}}>
In both cases, the event is handled by the LiveEvent.handle_emit/4
callback.
# On a LiveView OR LiveComponent
def handle_emit(:on_selected, {MyComponent, "foo"}, _payload, socket), do: ...
Example
defmodule MyLiveView do
use Phoenix.LiveView
use LiveEvent.LiveView
def render(assigns) do
~H"""
<.live_component module={MyLiveComponent} id="my-component" on_selected={self()} />
"""
end
def handle_emit(:on_selected, {MyLiveComponent, "my-component"}, %{at: at}, socket) do
IO.puts("Selected at #{at}")
{:ok, socket}
end
end
defmodule MyLiveComponent do
use Phoenix.LiveComponent
use LiveEvent.LiveComponent
def render(assigns) do
~H"""
<button phx-click="click" phx-targt={@myself}>Click me</button>
"""
end
def handle_emit("click", _, socket) do
{:noreply, emit(socket, :on_selected, %{at: DateTime.utc_now()})}
end
end
Link to this section Summary
Callbacks
Handle an event message sent by emit/3
or send_event/4
.
Functions
Raise an event from a LiveView or LiveComponent.
Send an event to a LiveView or LiveComponent.
Link to this section Callbacks
@callback handle_emit( name :: atom(), source :: any(), payload :: any(), socket :: LiveView.Socket.t() ) :: {:ok, socket :: LiveView.Socket.t()}
Handle an event message sent by emit/3
or send_event/4
.
Events sent via emit/3
have a source
argument of the form {module, id}
.
compared-to-handle_emit-3
Compared to handle_emit/3
This callback is distinct from LiveView's handle_emit/3
callback in a few important ways:
- The arity is different
- The result is
{:ok, socket}
, not{:noreply, socket}
- LiveEvent uses atoms for event names, not strings
- LiveEvent always originate from the server, not the client
example
Example
def handle_emit(:on_profile_selected, {MyLiveComponent, _id}, profile_id, socket), do: ...
Link to this section Functions
@spec emit( socket :: Phoenix.LiveView.Socket.t(), event_name :: atom(), payload :: any() ) :: Phoenix.LiveView.Socket.t()
Raise an event from a LiveView or LiveComponent.
The event_name
argument is the name of the optional socket assign whose value specifies the destination for the event.
The name of the emitted event defaults to the name of the assign.
Possible assign values are:
nil
to not raise the event- a pid, to send the event to a LiveView
{pid, event_name}
to send the event to a LiveView with a custom event name{module, id}
to send the event to a LiveComponent{module, id, event_name}
to send the event to a LiveComponent with a custom event name
Returns the unmodified socket.
@spec send_event( destination :: LiveView.Event.destination(), event_name :: atom(), payload :: any(), opts :: keyword() ) :: :ok
Send an event to a LiveView or LiveComponent.
Typically, emit/3
should be used to send events, but this function can be used if more control is needed.
To send to a LiveView (or any other process), specify a pid (usually self()
) as the destination
.
To send to a LiveComponent, specify {module, id}
as the destination
.
The event can handled by the LiveEvent.handle_emit/4
callback.
When sending to an arbitrary process, the message will be a LiveEvent.Event
struct, although you
should not normally have to deal with that directly.
options
Options
:source
- where the event originated from; defaults tonil
examples
Examples
send_event(self(), :on_selected, %{profile_id: 123})
# => def handle_emit(:on_selected, _source, %{profile_id: id}, socket), do: ...
send_event({MyComponent, "my-id"}, :on_selected, %{profile_id: 123})
# => def handle_emit(:on_selected, _source, %{profile_id: id}, socket), do: ...