4

Suppose I have a Phoenix app that includes a realtime dashboard of data.

I have a channel called MyApp.DashboardChannel, with a single topic of dashboard.

In MyApp.DashboardChannel, I have a function like:

def send_update do
  MyApp.Endpoint.broadcast(
    "dashboard",
    "update",
    %{data: MyApp.get_dashboard_data}
  )
end

Whenever something happens to modify the data being displayed in the dashboard, I can call this function to push out an updated version. For instance, in MyApp.TransactionController.create, after a transaction is saved, I can call DashboardChannel.send_update so that everyone will see the new transaction.

Now, suppose I want to modify this so that each dashboard user gets customized data. Essentially I want to say this: "for each connected user, run a query using their user_id and push the resulting data to them."

How can I do this?

I'm pretty sure the answer will involve Phoenix.Presence. I've already added it as the docs show, and am successfully getting presence_state and presence_diff events in the browser.

However:

  • I can't use Presence.list(socket) when calling send_update, because I'm calling it from a controller which has no access to a socket
  • When I call Presence.list(MyApp.DashboardChannel, "dashboard") I get an ** (EXIT) no process from GenServer. Am I misunderstanding how to use this?
Bruno Paulino
  • 5,611
  • 1
  • 41
  • 40
Nathan Long
  • 122,748
  • 97
  • 336
  • 451
  • I suppose I could broadcast a message that says "ask for an update!" and let each browser do an HTTP request specifying its user's id. But that's super hacky. – Nathan Long Nov 11 '16 at 21:51

1 Answers1

1

If you need to broadcast different data to each connected socket on a channel, the best way I know to do it is to drop down to PubSub:

Phoenix.PubSub.broadcast(
  MyApp.PubSub,
  # Could be any channel ID
  "dashboard",
  # Could be an atom or a tuple with some data attached,
  # of the sort you would pass to a GenServer's `handle_info/2`
  :update
)

Then, in your channel module, you'll need a new handle_info/2 callback to handle that event. Something like:

def handle_info(:update, socket) do
  payload = make_update_payload(socket.assigns.user_id)
  push(socket, "update", payload)
end

Since this is using Phoenix.Channel.push/3 rather than, say, broadcast/3, each connected user will get their own, personalized payload.

s3cur3
  • 2,749
  • 2
  • 27
  • 42