In my previous blog on the Note-taking app, we missed harnessing the most important feature of Phoenix LiveView i.e real-time update. What does that mean? It means to do an update in your application screen without explicitly refreshing your application. For example, in a chat app, you don't have to explicitly refresh your screen to see the change in the screen. In our previous version of the Notetaking app when we add the note in the screen and if there is a browser screen opened parallel with the existing one then that new note won't be reflected, you have to refresh the browser to see the result. In this blog, we are going to add this new feature to our previous blog code.
Refer to the gif
below for the complete real-time update version of the app that we are going to make.
How PubSub works? ๐ค
Phoenix PubSub is an API that is responsible for doing live updates in the app. The documentation itself says it's a Realtime Publisher/Subscriber service
. In application, our LiveView(module) process subscribes to a particular channel(s). When a message is published on a topic, the message is broadcast. Similarly, when a message is published from a topic, the message is broadcast to all the subscribers of that topic. When the broadcast message is received to the subscribed Process their state is updated which ultimately re-renders the view. Below diagram will give you an idea about subscribers and
Subscriber
Publisher
Adding the Subscriber
- We've understood the concept of the subscriber. It will be a function that will be called by the LiveView module.
- The
Phoenix.PubSub
module has asubscribe
function that takes 2 parameters.- The PubSub instance of our app, in our case it is
NoteApp.PubSub
- The name of the channel/topic, in our case it is "notes"
- The PubSub instance of our app, in our case it is
- Question arises where do we place this function. Ideally, it should be placed in the context API layer but we haven't created any context API layer. So, we will create a new module
lib/note_app/notes/note.ex
and the following code.defmodule NoteApp.Notes.Note do def subscribe() do Phoenix.PubSub.subscribe(NoteApp.PubSub, "notes") end end
Publishing/Broadcasting the message
- We know that PubSub has a
subscribe
function, likewise, it also has abroadcast
function in it. - We have to call this function to
publish/broadcast
the message. - As per our requirement we want to publish the message(note) as soon as we create the note.
- So our logic will reside in our
create_note
callback just after creating the note. - We need to pass the message as a tuple with the first field as the
message name
(as an atom) in our case it will be:create_note
and the other field is thenote
created.
lib/note_app/notes/note_server.ex
def handle_cast({:create_note, note}, notes) do
updated_note = add_id(notes, note)
updates_notes = [updated_note | notes]
message = {:note_created, updated_note}
#broadcast logic -start
Phoenix.PubSub.broadcast(
NoteApp.PubSub,
"notes",
message
)
#broadcast logic -end
{:noreply, updates_notes}
end
- The
broadcast
function takes 3 parameters.- The PubSub instance of our app, in our case it is
NoteApp.PubSub
- The name of the channel/topic, in our case it is "notes"
- The
message
we want tobroadcast
.
- The PubSub instance of our app, in our case it is
Subscribing to the Topic/Channel
- Any LiveView process which needs to be updated has to subscribe to the topic/channel.
- We have already created a
notes
topic. - Our
NotesLists
LiveView needs to subscribe to this topic/channel. - This is achieved very easily, we just need to add call the
subscribe
function in themount
callback. - But there is a caveat, we can only call the
subscribe
function when our LiveView is connected tosocket
. Also, we need to alias theNote
module to uselib/note_app_web/live/notes_index_live.ex
defmodule NoteAppWeb.NotesIndexLive do alias NoteApp.Notes.Note def mount(_params, _session, socket) do if connected?(socket), do: Note.subscribe() ..... ..... ..... end end
- We know LiveView is nothing but a GenServer process, so when the publisher broadcasts the message our LiveView process needs to handle that message. We will achieve that with the
handle_info
callback in ourNotesList
LiveView module. Add the following function in the module.
lib/note_app_web/live/notes_index_live.ex
defmodule NoteAppWeb.NotesIndexLive do
........
........
........
def handle_info({:note_created, note}, socket) do
socket = update(socket, :notes, fn notes -> [note | notes] end)
{:noreply, socket}
end
end
- We pattern match the
broadcast
message and retrieve thenote
and update thenotes
list in the socket with the latest message.
I hope you like this easy implementation of using Phoenix PubSub for achieving live updates. If you have any questions then please comment below.