LiveViewEvents provides a set of tools to send events between components.
Add live_view_events
to your list of dependencies in mix.exs
like:
def deps do
[
{:live_view_events, "~> 0.1.0"}
]
end
Add use LiveViewEvents
whenever you want to use any of the features of the libraries.
You can send events to LiveView components by using notify_to/2
or notify_to/3
(the only
difference being that the latter sends some extra params). These functions accept a target as
first argument and a message name as second. Targets can be any of:
nil
makes the function call be a noop. Useful if you want to only send a message if an explicit target has been passed in.:self
to send toself()
.- A PID.
- A tuple of the form
{Module, "id"}
to send a message to aLiveView.Component
in the same process. - A tuple of the form
{pid, Module, "id"}
to send a message to aLiveView.Component
in a different process.
You an handle these messages on the handle_info/2
callback on
your LiveView components. For being able to do so, you need to
use the handle_info_or_assign/2
macro on the update/3
callback of your component.
In our view, we are going to have two components. The first one is a Sender
that can send an event to whatever is being passed. Let's dig into the
implementation:
defmodule MyAppWeb.Components.Sender do
use MyAppWeb, :live_component
use LiveViewEvents
def mount(socket) do
socket = assign(socket, :notify_to, :self)
{:ok, socket}
end
def render(assigns) do
~H[<button type="button" phx-click="clicked" phx-target={@myself}>Send event</button>]
end
def handle_event("clicked", _params, socket) do
notify_to(socket.assigns.notify_to, :sender_event, :rand.uniform(100))
{:noreply, socket}
end
end
This component will send a :sender_event
message to whatever we pass to the notify_to
attribute. It sends a random number between 0 and up to 100 as parameter.
Let's dig into the receiver:
defmodule MyAppWeb.Components.Receiver do
use MyAppWeb, :live_component
use LiveViewEvents
def mount(socket) do
socket = assign(socket, :messages, [])
{:ok, socket}
end
def update(assigns, socket) do
socket = handle_info_or_assign(socket, assigns)
{:ok, socket}
end
def render(assigns),
do: ~H[<ul><li :for={m <- @messages}><%= m %></li></ul>]
def handle_info({:sender_event, num}, socket) do
{:noreply, update(socket, :messages, &[num | &1])}
end
end
This component will receive messages and handle them in handle_info/2
because
it is using handle_info_or_assign/2
in their LiveComponent.update/2
.
It will add the received messages to socket.assigns.messages
and display
them. As the reader can see, it is pattern matching against :sender_event
messages.
When notify_to/3
is used, the message sent is a tuple containing the event name
as first element, and the params as second the second element.
Finally, let's take a look at what the live view template would need to look like for this to work:
<div class="contents">
<.live_component
module={MyAppWeb.Components.Sender}
id="sender"
notify_to={{MyAppWeb.Components.Receiver, "receiver"}}
/>
<.live_component module={MyAppWeb.Components.Receiver} id="receiver" />
</div>
In this template, we set notify_to
to the tuple {MyAppWeb.Components.Receiver, "receiver"}
.
The first element of the tuple is the live component module and the second is the id.
Optionally, the tuple can contain an extra first element that needs to be a PID. Though
this might not be useful in the application code (there are way better ways to send events
between processes), it is quite useful when testing. When testing a LiveView
it creates a new process for it. Its PID can be accessed through view.pid
.
To normalize how to handle messages. If we could have handle_info("message_name", _socket)
or handle_info({"message_name", _params}, socket)
, you would need to think about whether
the message contains params or not and then use a tuple. With this normalization, you only
care about the message name and whether you need the params or not. Also, after using similar
approaches in the past, sometimes you want to send params and sometimes you don't need to.
In cases like these, it is helpful not having a different behaviour notifying an event without
params and sending an event with empty params.