Tutorial

View on Github

Send events from JS to a LiveView component

This post was updated 01 May - 2020

csrfjavascriptliveviewphoenix

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

Phoenix Boilerplate

Generate a Phoenix Boilerplate and save hours on your next project.

Try now

SAAS Starter Kit

Get started and save time and resources by using the SAAS Starter Kit built with Phoenix and LiveView.

Learn More

Related Tutorials

Published 03 Mar - 2020

Share LiveView state between tabs

javascriptliveviewphoenix

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..

Published 28 Jan - 2020

Phoenix LiveView and Invalid CSRF token

csrfformsliveviewphoenix

One issue that is common to run into is a CSRF error when posting some sort of form rendered with LiveView. The issue is that a LiveView component i..