Tutorial
View on GithubSend events from JS to a LiveView component
This post was updated 01 May - 2020
Let say you app uses a javascript library that needs to interact with your app. For example a LiveView component. That is possible with the built in Phoenix Channels and Phoenix PubSub.
STEP 1 - Generate a channel
Generata a new channel called App
mix phx.gen.channel App
In user_socket.ex
add:
channel "app:*", TutorialWeb.AppChannel
Update the AppChannel
component so it look like:
defmodule TutorialWeb.AppChannel do
use TutorialWeb, :channel
def join("app:" <> token, _payload, socket) do
{:ok, assign(socket, :channel, "app:#{token}")}
end
# can be triggered by from the frontend js by:
# channel.push('paginate', message)
def handle_in("paginate", payload, socket) do
Phoenix.PubSub.broadcast(Tutorial.PubSub, socket.assigns.channel, {"paginate", payload})
{:noreply, socket}
end
def handle_info(_, socket), do: {:noreply, socket}
end
NOTE. I want every page request to have a unique channel name. And that channel name needs to be exposed to any LiveView component. So fot this purpose, I will use the csrf-token
than is generated in the page header on every request.
In javascript, I can to this with
import {Socket} from "phoenix"
let socket = new Socket("/socket", {params: {token: window.userToken}})
socket.connect()
let channel = socket.channel(`app:${document.querySelector("meta[name='csrf-token']").getAttribute("content")}`, {})
channel.join()
.receive("ok", resp => { console.log("Joined successfully", resp) })
.receive("error", resp => { console.log("Unable to join", resp) })
Now, I have a channel name that looks something like:
app:FxBlXhI8Kj0WSQUrCi0vX0UIAhguFVZ9gZ5spuCEfqAg9Ou6radTlX01
So, If I send a channel event in JS to the channel, I have a handle_in-function that handles the event
def handle_in("paginate", payload, socket) do
Phoenix.PubSub.broadcast(Tutorial.PubSub, socket.assigns.channel, {"paginate", payload})
{:noreply, socket}
end
And in this function, I use Phoenix PubSub to broadcast the event to any subscribers.
STEP 2 - Subscribe to javascript events
Its easy to set up the a LiveView component to subscribe and handle events from Phoenix PubSub. In the last tutorial, I set up a LiveView component for handling pagination. I want to revisit that component and set up a subscription. Open lib/tutorial_web/live/product_list_live.ex
and in the mount
-function add:
if connected?(socket), do: Phoenix.PubSub.subscribe(Tutorial.PubSub, "app:#{csrf_token}")
So it looks like:
def mount(%{"csrf_token" => csrf_token} = _session, socket) do
if connected?(socket), do: Phoenix.PubSub.subscribe(Tutorial.PubSub, "app:#{csrf_token}")
assigns = [
conn: socket,
csrf_token: csrf_token
]
{:ok, assign(socket, assigns)}
end
This means, when the component is connected, subscribe to anything that happens on the channel that corresponds to the curren csrf_token
. This means, that if the channel will be unique per browser request.
The last piece here is to actually handle the messages we subscribe to.
Add in the same file:
def handle_info({"paginate", %{"page" => page}}, socket) do
{:noreply, live_redirect(socket, to: Routes.live_path(socket, ProductListLive, page: page))}
end
def handle_info(_, socket), do: {:noreply, socket}
STEP 3 - Send the message from JS
Now, that the backend code is wired up, I can add the final piece of the javascript. I will visit the page: http://localhost:4000/products
and expect that I will send a pagination event after 5 seconds and navigate to page 3 http://localhost:4000/products?page=3
setTimeout(() => {
channel.push('paginate', {page: '3'})
}, 3000)
Final Result
After a few moments the time out in setTimeout
will kick in and the page will live_redirect to page 3
Tag Cloud
Phoenix Bolerplate
Generate a Phoenix Boilerplate and save hours on your next project.
SAAS Starter Kit
Get started and save time and resources by using the SAAS Starter Kit built with Phoenix and LiveView.
Subscribe for $39/mo to geat ahead!
Learn MoreRelated Tutorials
Published 03 Mar - 2020 - Updated 01 May - 2020
Share LiveView state between tabs
Each LiveView on each tab spawns a separate state. That might or might not be the desired behaviour. In this tutorial, I am going to share state bet..