For my project what I did was create a helper struct called Target
:
defmodule CoreWeb.Components.Helpers.Target do
@moduledoc """
A struct to be used when passing `LiveComponents` targets to other components.
"""
alias Phoenix.LiveView
use TypedStruct
typedstruct enforce: true do
field :id, String.t()
field :module, Module.t()
end
@spec new(String.t(), Module.t()) :: t
def new(id, module), do: struct!(__MODULE__, id: id, module: module)
@spec send_message(map, t() | nil, pid) :: any
def send_message(message, target, pid \\ self())
def send_message(message, nil, pid), do: send(pid, message)
def send_message(message, %__MODULE__{id: id, module: module}, pid),
do: LiveView.send_update(pid, module, Map.put(message, :id, id))
end
The way it works is that every component that would send messages to another component or liveview receives it as an attr:
attr :target, Target, default: nil
Then, when I use that component, I can pass the target
attr with something like this Target.new(@id, __MODULE__)
if I want to send to a component, and just not pass anything when I want to send to liveview.
Then, inside my component, I just use the send_message
function like this:
Target.send_message(%{operation: :clear_filter, filter_id: filter_id}, target)
In this case, if target is nil
, it will send to liveview and you can get it using:
def handle_info(%{operation: :clear_filter} = message, socket) do
...
end
Or to the component with:
def update(%{operation: :clear_filter} = message, socket) do
...
end