(Almost) Javascript-free Real-Time Web App Development with Phoenix Live View
By: Ridho Pratama
Tags: Elixir, Phoenix, Web Development, Phoenix Framework, Phoenix Liveview

So, for a moment imagine that you are me. Average run-off-the-mill software engineer that is only able to do backend stuffs, absolutely no speck of frontend skill, and now you want to make a web app, say, like some public chat room. The thing is, the modern frontend ecosystem is very confusing, what is *inhale* Webpack, Babel, React, Vue, Redux, SCSS (did I mention I literally have 0 CSS skill?), and many more. I know there’s CRA, but even if I accept using CRA (did I mention that CRA depends on at least1260 packages?), I still have to write a backend for the app. How do I make the front part talks to the back part? Do I need to create websocket handler manually? Longpolling? Whoa what is this new and shiny HTTP push. Needless to say I have low attention span and too lazy to do all of this.

In the other hand, you know Elixir and absolutely in love with it, and you wish you could use it anywhere, even for creating this chat web app. Turns out there’s a certain part of the Elixir community which also wishes for the same thing and made Phoenix LiveView.

Phoenix? LiveView?

First of all, what is Phoenix? Phoenix is a popular web framework for Elixir. It’s like Django for Python, Laravel for PHP, and Express for Node. The unique part is that it utilizes the Elixir and BEAM VM capability to bring interactive and real-time experience to web application without any of the client-side complexities. Broadcast message easily with Channels, know where your users are with Presence and many more.

Then there’s LiveView. With it you could easily have interactive frontend where you could change the state of your frontend easily with just changing the state in the backend. You could also send messages from client-side to the server-side (and vice versa) easily without having to thought about the transportation mechanism, it’s already abstracted over websocket. These makes developer like me able to write interactive web-app without having to write much javascript (so I still need to to CSS on my own)

Lets Do This

Generating Project

So after installing the Phoenix generator, let’s try to make a project and see how far we got, because this is my first time using Phoenix and LiveView.

First let’s make a new phoenix project. The --live flag tells the generator to also setup liveview for our phoenix project.

❯ mix phx.new chatter --live ... We are almost there! The following steps are missing: $ cd chatter 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

Running mix phx.server and opening http://localhost:4000/ should give us this.

Default homepage generated by Phoenix

Setting Up Database and Schema

After this change the database configuration in config/dev.exs and run mix ecto.create to setup the database. Ecto is the go-to ORM for elixir and it includes migration tools. Phoenix tools also came with ecto schema generator which ease generation of schema and the migration files. We imagine that our chat app will have a Message schema which will store our messages. Let’s generate this too.

❯ mix phx.gen.schema Chatroom.Message messages message:string room:string

* creating lib/chatter/chatroom/message.ex

* creating priv/repo/migrations/20201025122327_create_messages.exs Remember to update your repository by running migrations: $ mix ecto.migrate

After running mix ecto.migrate, open the message.ex file and add additional changeset, create and read functions for our new schema.

We could use iex -S mix to try creating and querying object.

❯ iex -S mix

Erlang/OTP 22 [erts-10.4.4] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [hipe] Compiling 1 file (.ex)

Interactive Elixir (1.10.3) - press Ctrl+C to exit (type h() ENTER for help)

iex(2)> Chatter.Chatroom.Message.create(%{"message" => "hello world", "room" => "default"})

{:ok,

%Chatter.Chatroom.Message{

__meta__: #Ecto.Schema.Metadata<:loaded, "messages">,

id: 1,

inserted_at: ~N[2020-10-25 12:38:47],

message: "hello world",

room: "default",

updated_at: ~N[2020-10-25 12:38:47]

}}

iex(3)> Chatter.Chatroom.Message.get_latest(100, "default")

[

%Chatter.Chatroom.Message{

__meta__: #Ecto.Schema.Metadata<:loaded, "messages">,

id: 1,

inserted_at: ~N[2020-10-25 12:38:47],

message: "hello world",

room: "default",

updated_at: ~N[2020-10-25 12:38:47]

}

]

Nice, our database is up and running, now let’s take care of the client part.

LiveView Part

Now to the scary part (for me at least). We want to have the main page to be our chat room, so if we take a look at the lib/chatter_web/router.ex the / route is handled by PageLive module and we want to change that. This time we just delete the contents of page_live.html.leex and almost everything from page_live.ex except for mount/3 method (the first one). Let’s run iex -S mix phx.server to check what we have done (keep this iex server running).

Now we get empty page (except the frame, but we’ll keep that because I’m too lazy)

First, let’s take care of showing the messages. Phoenix templates use socket assigns to pass data to the template. So let’s try querying the messages and assign it to the message_list key. While we’re at that let’s add a simple for loop to the template.

Let’s take a look at http://localhost:4000 again, we should see our “hello world” message without having to restart again.

Cool!

If we create a new message with the iex terminal like we’ve done before, we should see our new message when we refresh the page (don’t worry we could make the client automatically updates later).

Chatbox

Of course a chat app needs a way to enter message. For this we just use the usual html input with form, but we can use the phoenix form_for helper which makes creating form easier. Here is our first meeting with the event emitter which will be our way of sending event from the client to the server.

We handle events using handle_event/3 callback. With the validate event we update the changeset to validate our input and in the submit event we create the new message object and update our message_live assign.

At least it is something…

Updating Other Client

Now we have a chat app where we need to refresh manually to see new message, and of course we don’t want this. For this, we could use Phoenix PubSub to make the client listen to broadcast messages. So now we broadcast a message every time a message is created, and have the client subscribe to it. When a message created event came, we could have the client update the message list.

Restart our iex session and take a look at what we have created.

It’s working!

Closing

So we’ve taken a look at how easy (and importantly, without writing any Javascript) it is to create an interactive web application with Phoenix LiveView. If you want to see the code you could take a look at https://github.com/ridho9/chatter.

If these interest you then you can learn more from https://hexdocs.pm/phoenix/overview.html and https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html.

Thanks for reading!