Packages

A simple event bus for Phoenix LiveViews to send events between LiveViews and LiveComponents by ID

Current section

Files

Jump to
live_view_bus lib live_view_bus.ex
Raw

lib/live_view_bus.ex

defmodule LiveViewBus do
@moduledoc """
A simple event bus for Phoenix LiveViews to send events between LiveViews
and LiveComponents by ID—without manually wiring PubSub, topics, and
`handle_info` every time.
## Topic convention
All topics use the prefix `live_view_bus:` to avoid collisions with other
PubSub usage. For example, subscribing to ID `"products_list"` subscribes
to topic `"live_view_bus:products_list"`.
## Message format
Messages are delivered as:
{:live_view_bus, id, event, payload}
Where:
- `id` is the topic ID you subscribed to
- `event` is the event name (string)
- `payload` is the payload map (defaults to `%{}`)
## Example
# In your LiveView's mount:
def mount(_params, _session, socket) do
LiveViewBus.subscribe(MyApp.PubSub, "products_list")
{:ok, assign(socket, products: [])}
end
# In terminate (optional, Phoenix cleans up subscriptions when process ends):
def terminate(_reason, socket) do
LiveViewBus.unsubscribe(MyApp.PubSub, "products_list")
:ok
end
# Handle incoming events:
def handle_info({:live_view_bus, "products_list", "filters_changed", payload}, socket) do
products = load_products(payload)
{:noreply, assign(socket, products: products)}
end
# Send from another LiveView or Component:
LiveViewBus.send(MyApp.PubSub, "products_list", "filters_changed", %{
category: "electronics",
sort: "price"
})
"""
@doc """
Returns the PubSub topic for the given ID.
"""
@spec topic(String.t() | atom()) :: String.t()
def topic(id), do: "live_view_bus:#{id}"
@doc """
Subscribes the current process to the given ID.
The process will receive messages as `{:live_view_bus, id, event, payload}`.
## Examples
LiveViewBus.subscribe(MyApp.PubSub, "products_list")
LiveViewBus.subscribe(MyApp.PubSub, "dashboard")
"""
@spec subscribe(module() | pid(), String.t() | atom()) :: :ok | {:error, term()}
def subscribe(pubsub, id) do
Phoenix.PubSub.subscribe(pubsub, topic(id))
end
@doc """
Unsubscribes the current process from the given ID.
"""
@spec unsubscribe(module() | pid(), String.t() | atom()) :: :ok
def unsubscribe(pubsub, id) do
Phoenix.PubSub.unsubscribe(pubsub, topic(id))
end
@doc """
Sends an event to all processes subscribed to the given ID.
## Examples
LiveViewBus.send(MyApp.PubSub, "products_list", "filters_changed", %{
category: "electronics",
sort: "price"
})
LiveViewBus.send(MyApp.PubSub, "dashboard", "refresh")
"""
@spec send(module() | pid(), String.t() | atom(), String.t() | atom(), map()) :: :ok
def send(pubsub, id, event, payload \\ %{}) do
broadcast(pubsub, id, event, payload)
end
@doc """
Broadcasts an event to all processes subscribed to the given topic ID.
Same as `send/4` but with an explicit name for "broadcast to topic" semantics.
Use this when you want to send to a group topic (e.g. `"room_123"`).
## Examples
LiveViewBus.broadcast(MyApp.PubSub, "dashboard", "data_refreshed", %{
timestamp: DateTime.utc_now()
})
"""
@spec broadcast(module() | pid(), String.t() | atom(), String.t() | atom(), map()) :: :ok
def broadcast(pubsub, id, event, payload \\ %{}) do
message = {:live_view_bus, id, event, payload}
Phoenix.PubSub.broadcast(pubsub, topic(id), message)
:ok
end
end