'How to send events directly from Phoenix Live View to a component
I have a question about the Phoenix LiveView Component (v0.15.4).
In the documentation, there is an example code:
def handle_info({:updated_card, card}, socket) do
send_update CardComponent, id: card.id, board_id: socket.assigns.id
{:noreply, socket}
end
LiveView components do not have a handle_info/2 callback, so send_update/3 is called to redirect events from the parent LiveView to a specific component.
However, I want to send events directly from the parent LiveView to the specific component, because I want to do something like this
# parent LiveView
def handle_info({:new_card, card_id}, socket) do
send_event CardStackComponent, id: :card_stack, event: "add_card", params: %{"card_id" => card_id}
{:noreply, socket}
end
# component
def handle_event("add_card", %{"card_id" => card_id} = _params, socket) do
card = get_card(card_id)
socket =
update(socket, :cards, fn cards ->)
[card | cards]
end)
{:noreply, socket}
end
Of course, there is no such function as Phoenix.LiveView.send_event/2. The above is a fictional code to illustrate my intention.
I know how to send events from a client (browser) to a specific component, either by adding the phx-target="<%= @myself %>" attribute to an HTML element, or by using the pushEventTo method in a JavaScript program.
However, as far as I know, there is no way to send an event directly from a parent LiveView to a specific component.
Is there any workaround or better way?
Solution 1:[1]
Phoenix Pubsub is the answer.
defmodule QuickPick.Cards.LiveUpdates do
@topic inspect(__MODULE__)
@doc "subscribe for all cards"
def subscribe_live_view do
Phoenix.PubSub.subscribe(QuickPick.PubSub, topic(), link: true)
end
@doc "subscribe for specific card"
def subscribe_live_view(card_id) do
Phoenix.PubSub.subscribe(QuickPick.PubSub, topic(card_id), link: true)
end
@doc "notify for all cards"
def notify_live_view(message) do
Phoenix.PubSub.broadcast(QuickPick.PubSub, topic(), message)
end
@doc "notify for specific card"
def notify_live_view(card_id, message) do
Phoenix.PubSub.broadcast(QuickPick.PubSub, topic(card_id), message)
end
defp topic, do: @topic
defp topic(card_id), do: topic() <> to_string(card_id)
end
Subscribe and handle on updating
def mount(session, socket) do
LiveUpdates.subscribe_live_view(card.id)
...
end
def handle_info({_requesting_module, [:recommendations, :updated], []}, socket) do
...
end
Updating
LiveUpdates.notify_live_view(
card.id,
{__MODULE__, [:recommendations, :updated], []}
)
Solution 2:[2]
you can also do
# parent LiveView
def handle_info({:new_card, card_id}, socket) do
send_update CardStackComponent, id: :card_stack, card_id: card_id
{:noreply, socket}
end
#component
def update(%{card_id: card_id} = _assigns, socket) do
card = get_card(card_id)
socket =
update(socket, :cards, fn cards ->
[card | cards]
end)
{:ok, socket}
end
you have to have in mind that update/2 is going to be called on each render. More info about this here
Is also worth mention that you may want to maintain the state either in the LiveView or the LiveComponent but not both, as is mentioned here
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |
