Elixir Phoenix PubSub Tutorial: Difference between revisions

From ElixirBlocks
Jump to: navigation, search
No edit summary
No edit summary
 
(55 intermediate revisions by the same user not shown)
Line 2: Line 2:
__TOC__
__TOC__
<hr>
<hr>
'''Prerequisites'''
You should know how to launch a 1 page Live View app that lets the user submit data via a form. The form data should print to the page.
<hr/>
This tutorial explains the basics of building a Phoenix PubSub chat app.  
This tutorial explains the basics of building a Phoenix PubSub chat app.  


The goal of this writing is to clarify code used to build PubSub applications in Phoenix. 
===What is PubSub?===
Using the PubSub feature of Phoenix is not difficult and this tutorial could be less verbose, I wrote it this way to give the reader the most context with the least amount of code as possible. My goal is to ensure that you understand how to integrate PubSub basics into any Phoenix app.
PubSub is the Phoenix frameworks real time publish/subscribe service. This is an API that lets developers write applications that update in near real time such as chat rooms and multi-player games.
 
 
The goal of this writing is to explain how to use the code provided by the PubSub API to build real time applications.  
 


===Project Description===
The tutorial is divided into three parts, each part building on the completion of the previous one.
The tutorial is divided into three parts, each part building on the completion of the previous one.


'''Project Description'''
* '''Part 1''': We create a two route live view app with each route assigned its own page. The two routes are '''/send''' and '''/receive'''.
 
* '''Part 1''': We create a two route liveview app with each route assigned its own page. The two routes are '''/send''' and '''/receive'''
The page at '''/send''' contains an html form and form submission code. When data is submitted, the '''/receive''' page receives the data, and renders it to its page. The code we write for this exercise uses pubsub functions to connect the two LiveView pages.  
The page at '''/send''' contains an html form and form submission code. When data is submitted, the '''/receive''' page receives the data, and renders it to its page. The code we write for this exercise uses pubsub functions to connect the two LiveView pages.  


When complete, you will have two browser tabs open, one opened to '''/send''' and one opened to '''/receive'''. The '''/send''' route sends data to '''/receive''' and you view the update in real time.
When complete, you will have two browser tabs open, one opened to '''/send''' and one opened to '''/receive'''. The '''/send''' route sends data to '''/receive''' and you view the update in real time.


* '''Part 2''': We convert the previous app into a single LiveView page that performs both actions on a single route. The end result of this exercise is the creation of a single page chat application where data updates are viewable in real time across browser tabs.
* '''Part 2''': We convert the previous app into a single LiveView page that performs both actions on a single route. The end result is the creation of a single page chat application containing real time updates that you view across browser tabs.
 
* '''Part 3''': We write code to update your single page app to store chat data in a database.
* '''Part 3''': We write code to update your single page app to store chat data in a database.




Line 66: Line 82:
Go to the router and update it as shown.
Go to the router and update it as shown.


app/app_web/router.ex
'''directory:'''  app/app_web/router.ex


<source>
<source>
Line 79: Line 95:
== Create the LiveView Pages ==
== Create the LiveView Pages ==


Create a new folder named live in this directory: '''App/lib/app_web'''. The end result will look like this: '''App/lib/app_web/live'''.
Create a new folder named live in this directory: '''App/lib/app_web'''. The end result looks like this: '''App/lib/app_web/live'''.


In the '''live''' directory, create two files and name them '''send_live.ex''' and '''receive_live.ex'''.
In the '''live''' directory, create two files and name them '''send_live.ex''' and '''receive_live.ex'''.
Line 95: Line 111:
   def mount(_params, _session, socket) do
   def mount(_params, _session, socket) do
     {:ok, socket}
     {:ok, socket}
  end
  def handle_event("send", %{"text" => text}, socket) do
    IO.inspect(text)
    Phoenix.PubSub.broadcast(App.PubSub, "message", {:pubsub_transmission, text})
    {:noreply, socket}
   end
   end


Line 101: Line 125:
     ~H"""
     ~H"""
     <div>
     <div>
       <h1>Send Connection</h1>
       <h1>Send Message</h1>
 
      <form phx-submit="send">
   
        <input type="text" name="text" />
        <button type="submit">Send</button>
      </form>
     </div>
     </div>
     """
     """
Line 111: Line 137:
</source>
</source>


==Edit Receive_Live.ex ==


Open the file named receive_live.ex and paste the following code into it:
Open receive_live.ex and copy the following code to it.


<source>
<source>
defmodule AppWeb.ReceiveLive do
defmodule AppWeb.ReceiveLive do
   use AppWeb, :live_view
   use AppWeb, :live_view


   def mount(_params, _session, socket) do
   def mount(_params, _session, socket) do
     {:ok, socket}
    if connected?(socket) do
      Phoenix.PubSub.subscribe(App.PubSub, "message")
    end
 
     {:ok, assign(socket, message_item: "")}
  end
 
  def handle_info({:pubsub_transmission, text}, socket) do
    {:noreply, assign(socket, message_item: text)}
   end
   end


Line 127: Line 159:
     ~H"""
     ~H"""
     <div>
     <div>
       <h1>Receive Connection</h1>
       <h1>ChatLive</h1>
 
      <%= @message_item %>
 
     </div>
     </div>
     """
     """
   end
   end
end
end
</source>
== Validate Your Work ==
Ensure the code you wrote has no errors by running the server and checking each route.
mix phx.server
Go to: 
localhost:4000/send
localhost:4000/receive
The pages should load without error.


== Creating a Form and its Event Handler ==
The first step to send data via a form is to create the form and write an event handler to listen and receive data from the form. You should have enough prerequisite knowledge to know how to do this. If you don't read this tutorial before you proceed:
[[Forms and Event Handlers in Elixir Phoenix|Forms and Event Handlers in Elixir Phoenix]]
Update the send_live.ex to reflect the code below:
<source>
defmodule AppWeb.SendLive do
  use AppWeb, :live_view
 
  def mount(_params, _session, socket) do
    {:ok, socket}
  end
 
 
  def handle_event("send", %{"text" => text}, socket) do
    IO.inspect text
    {:noreply, socket}
  end
 
  def render(assigns)do 
  ~H"""
<div>
  <h1>Send Message</h1>
  <form phx-submit="send">
    <input type="text" name="text" />
    <button type="submit">Send</button>
  </form>
</div>
"""
  end
 
end


</source>
</source>


Ensure it works by submitting form data. In the terminal console you will see the output.


== Before We Add the PubSub Code ==


You are now going to perform these actions.


1. Add code designed to broadcast a change
Launch the server and open up two browser tabs. Set one to '''/send''' and the other to '''/receive'''


2. Add code designed to subscribe to that change
Submit data from '''/send''' and view the result on '''/receive'''.


Both of these code pieces act similar to event handlers/listeners in that they listen to events that the developer designates and performs an action in response.
Play with the code and explore it. Ensure that it works.


Both broadcast code and subscribe code can be placed in different parts of your codebase depending on your goal.
===Explanation===


For this exercise you write the broadcast code to your '''SendLive''' module and write the subscribe code to your '''ReceiveLive''' module.
Before explaining the code, the following summarizes the important parts applicable to all PubSub applications. Direct your attention to the three functions below. These functions are the main tools that you use when working with the PubSub API and it is important to get familiar with them.


===Add the PubSub Broadcast Code to SendLive Module===
* Phoenix.PubSub.'''broadcast'''(App.PubSub, "message", {:pubsub_transmission, text})
* Phoenix.PubSub.'''subscribe'''(App.PubSub, "message") 
* ''' handle_info'''({:pubsub_transmission, text}, socket)


<source>
For brevity I refer to these as '''broadcast''','''subscribe''' and '''handle_info'''.


All PubSub applications are composed of these three functions.


defmodule AppWeb.SendLive do
===A Simple Mental Model===
  use AppWeb, :live_view
 
  def mount(_params, _session, socket) do
    {:ok, socket}
  end
 
 
  def handle_event("send", %{"text" => text}, socket) do
    IO.inspect text
    AppWeb.Endpoint.broadcast(topic, "message", text) # Broadcast
    {:noreply, socket}
  end
 


  defp topic do #Topic
Mental Models are helpful not because they are 100 percent accurate but because they are tangibly useful. The mental model I use is as follows:
    "chat"
  end


The '''broadcast''' and '''subscribe''' functions are used to send data from one module to another. You can look at these functions as two ends of a physical cable that connects modules together.
The module(s) that transmits the data must contain the '''broadcast''' function in it's code, the module(s) receiving the data must contain the '''subscribe''' function in it's code.


  def render(assigns)do 
The receiving module must also contain a handle_info function. This function acts as an event handler that listens for and captures the data.
  ~H"""
<div>
  <h1>Send Message</h1>


  <form phx-submit="send">
    <input type="text" name="text" />
    <button type="submit">Send</button>
  </form>
</div>


"""
  end
 
end
</source>


===Add the PubSub Subscribe Code to ReceiveLive Module===
====Phoenix.PubSub.broadcast====


1. In SendLive the code Phoenix.PubSub.broadcast is used to broadcast message data. This method takes three arguments.


<source>
* The pubsub type
* The topic
* The payload (the data you want to send to all listeners)


The pubsub type is always going to be named your-app dot PubSub (such as App.PubSub) For our purposes you can treat this argument as boiler plate code. 


defmodule AppWeb.ReceiveLive do
Phoenix.PubSub.broadcast('''App.PubSub''')
  use AppWeb, :live_view
 
  def mount(_params, _session, socket) do




    if connected?(socket) do
The second argument, called the topic,  is a string that represents a connection between the broadcast function and a subscribe function. The topic connects them together.
      AppWeb.Endpoint.subscribe(topic)    # PupSub Subscribe
    end


    {:ok, assign(socket, messages: "")}
Phoenix.PubSub.broadcast( App.PubSub, '''"some-topic-goes-here"''' )
  end
 
  def handle_info(%{event: "message", payload: message}, socket) do  # Handle Ifno is needed
    IO.inspect message
    {:noreply, assign(socket, messages: message)}
  end
 


  defp topic do  # Topic
The third argument is the payload. This can be any data you want to transmit between Live Views.
    "chat"
  end


  def render(assigns)do 
  ~H"""
    <div>
    <h1>ChatLive</h1>
    <%= @messages %>


Phoenix.PubSub.broadcast( App.PubSub, "some-topic-goes-here",'''%{data: "hello world}''')


    </div>
====Phoenix.PubSub.subscribe====
The first two arguments of subscribe mirror broadcast.


    """
* Phoenix.PubSub. subscribe ('''App.PubSub, topic''')
  end
 
end
</source>
 
== PubSub Code Result and Explanation==
Open the app in to broser tabs and set one to /send and one to /receive.
Submit a form in /send and you will see the result in /receive
 
=== SendLive PubSub Code Explained===
The SendLive module contains this code:
 
<source>
AppWeb.Endpoint.broadcast(topic, "message", text)
</source>
 
In all default Phoenix applications the PubSub code is in the module named Endpoint.
 
<source>
NameOfApp.Endpoint
</source>
 
The broadcast function has three arguments. In our examples they are named topic, "message" and text.
 
<source>
AppWeb.Endpoint.broadcast(topic, "message", text)  
</source>
 
The first argument is the name of the topic. The topic is what connects the broadcast and the subscriber. You can have many broadcasters and subscribers all with different topics.
 
A topic is a string that acts as an ID to connect the broadcast and subscriber. In our example the topic is "chat" and is referenced in a private function named topic.
 
<source>
 
  defp topic do #Topic
    "chat"
  end
 
 
 
</source>


The code will work if you place the string in the broadcast function directly, like this:
To use subscribe in a Module, you place it in the '''mount''' function.


AppWeb.Endpoint.broadcast('''"chat"''', "message", text)
====handle_info====


=== ReceiveLive PubSub Code Explained===
This is a function that is invoked on any "send" event to a process. In our code it is used as a tool to capture the data being subscribed to.

Latest revision as of 07:29, 29 October 2023

This page is in progress


Prerequisites

You should know how to launch a 1 page Live View app that lets the user submit data via a form. The form data should print to the page.




This tutorial explains the basics of building a Phoenix PubSub chat app.

What is PubSub?

PubSub is the Phoenix frameworks real time publish/subscribe service. This is an API that lets developers write applications that update in near real time such as chat rooms and multi-player games.


The goal of this writing is to explain how to use the code provided by the PubSub API to build real time applications.


Project Description

The tutorial is divided into three parts, each part building on the completion of the previous one.

  • Part 1: We create a two route live view app with each route assigned its own page. The two routes are /send and /receive.

The page at /send contains an html form and form submission code. When data is submitted, the /receive page receives the data, and renders it to its page. The code we write for this exercise uses pubsub functions to connect the two LiveView pages.

When complete, you will have two browser tabs open, one opened to /send and one opened to /receive. The /send route sends data to /receive and you view the update in real time.

  • Part 2: We convert the previous app into a single LiveView page that performs both actions on a single route. The end result is the creation of a single page chat application containing real time updates that you view across browser tabs.
  • Part 3: We write code to update your single page app to store chat data in a database.



Part 1

Setup

To begin we will start with an new Phoenix instance. In your terminal create a new phoenix app by typing:

mix phx.new app

When the console prompts you to Fetch and install dependencies, choose yes.

When you see the following instructions configure your database, run mix ecto.create.



We are almost there! The following steps are missing:

    $ cd app

Then configure your database in config/dev.exs and run:

    $ mix ecto.create

Start your Phoenix app with:

    $ mix phx.server

You can also run your app inside IEx (Interactive Elixir) as:

    $ iex -S mix phx.server



If you are new to Elixir and are having trouble with database setup, please fix those issues before proceeding with this tutorial.

When the app is created, open your web browser to localhost:4000. The app will launch. If it doesn't, fix the errors before moving forward.


Create Routes

Go to the router and update it as shown.

directory: app/app_web/router.ex

  scope "/", AppWeb do
    pipe_through :browser
    live "/send", SendLive, :home
    live "/receive", ReceiveLive, :home
    get "/", PageController, :home
  end

Create the LiveView Pages

Create a new folder named live in this directory: App/lib/app_web. The end result looks like this: App/lib/app_web/live.

In the live directory, create two files and name them send_live.ex and receive_live.ex.


Edit Send Live.ex

Open the file send_live.ex in your text editor. Paste the following code into it.

defmodule AppWeb.SendLive do
  use AppWeb, :live_view

  def mount(_params, _session, socket) do
    {:ok, socket}
  end

  def handle_event("send", %{"text" => text}, socket) do
    IO.inspect(text)

    Phoenix.PubSub.broadcast(App.PubSub, "message", {:pubsub_transmission, text})

    {:noreply, socket}
  end


  def render(assigns) do
    ~H"""
    <div>
      <h1>Send Message</h1>
      <form phx-submit="send">
        <input type="text" name="text" />
        <button type="submit">Send</button>
      </form>
    </div>
    """
  end
end


Open receive_live.ex and copy the following code to it.

defmodule AppWeb.ReceiveLive do
  use AppWeb, :live_view

  def mount(_params, _session, socket) do
    if connected?(socket) do
      Phoenix.PubSub.subscribe(App.PubSub, "message")
    end

    {:ok, assign(socket, message_item: "")}
  end

  def handle_info({:pubsub_transmission, text}, socket) do
    {:noreply, assign(socket, message_item: text)}
  end

  def render(assigns) do
    ~H"""
    <div>
      <h1>ChatLive</h1>
      <%= @message_item %>
    </div>
    """
  end
end



Launch the server and open up two browser tabs. Set one to /send and the other to /receive

Submit data from /send and view the result on /receive.

Play with the code and explore it. Ensure that it works.

Explanation

Before explaining the code, the following summarizes the important parts applicable to all PubSub applications. Direct your attention to the three functions below. These functions are the main tools that you use when working with the PubSub API and it is important to get familiar with them.

  • Phoenix.PubSub.broadcast(App.PubSub, "message", {:pubsub_transmission, text})
  • Phoenix.PubSub.subscribe(App.PubSub, "message")
  • handle_info({:pubsub_transmission, text}, socket)

For brevity I refer to these as broadcast,subscribe and handle_info.

All PubSub applications are composed of these three functions.

A Simple Mental Model

Mental Models are helpful not because they are 100 percent accurate but because they are tangibly useful. The mental model I use is as follows:

The broadcast and subscribe functions are used to send data from one module to another. You can look at these functions as two ends of a physical cable that connects modules together. The module(s) that transmits the data must contain the broadcast function in it's code, the module(s) receiving the data must contain the subscribe function in it's code.

The receiving module must also contain a handle_info function. This function acts as an event handler that listens for and captures the data.


Phoenix.PubSub.broadcast

1. In SendLive the code Phoenix.PubSub.broadcast is used to broadcast message data. This method takes three arguments.

  • The pubsub type
  • The topic
  • The payload (the data you want to send to all listeners)

The pubsub type is always going to be named your-app dot PubSub (such as App.PubSub) For our purposes you can treat this argument as boiler plate code.

Phoenix.PubSub.broadcast(App.PubSub)


The second argument, called the topic, is a string that represents a connection between the broadcast function and a subscribe function. The topic connects them together.

Phoenix.PubSub.broadcast( App.PubSub, "some-topic-goes-here" )

The third argument is the payload. This can be any data you want to transmit between Live Views.


Phoenix.PubSub.broadcast( App.PubSub, "some-topic-goes-here",%{data: "hello world})

Phoenix.PubSub.subscribe

The first two arguments of subscribe mirror broadcast.

  • Phoenix.PubSub. subscribe (App.PubSub, topic)

To use subscribe in a Module, you place it in the mount function.

handle_info

This is a function that is invoked on any "send" event to a process. In our code it is used as a tool to capture the data being subscribed to.