Elixir LiveView Countdown Clock

by

Pushing a Boulder Uphill

If you’ve ever worked on a large application, you know that it is easy for it to become very complex very quickly. You’ve likely found yourself building a javascript single-page application in React, Angular, or Vue. Using Webpack to bundle your distribution. Adding  NodeJS to an existing backend infrastructure to expose an easier-to-use GraphQL application programming interface (API). Then, adding pre-rendering to optimize your front-end.

History of LiveView

Chris McCord, the creator of Phoenix LiveView grew tired of this complicated endeavour, and decided to create a framework that could simplify all of this into one, easy-to-understand, product. I will be demonstrating how easy it is to get started building an Elixir application with Phoenix LiveView.

Distributed, Low Latency, and Fault-Tolerant

Elixir is a dynamic functional language that is known for having low latency, and being distributed and fault-tolerant. Elixir is nicely complemented by Phoenix LiveView, a technology on the Phoenix framework that is WebSocket based, provides server-rendered UI, and aims to reduce stack complexity.

CLI Generator

We will use a command line tool that generates the code for this project, rather than referencing code in a library. We are going to generate a new Phoenix project named “clock.” Simply type mix phx.new clock --live --no-ecto. The --live option tells Phoenix to install LiveView and build an example LiveView page. The --no-ecto option tells Phoenix to not install any dependencies or setup for database connections. Phoenix will print out all of the files that are automatically generated.

Distributed

The first thing you will notice is that there are two folders. While most projects have all of their source code in one place, the Elixir community loves distribution of concepts, so from the beginning, it breaks your project into two pieces: clock and clock_web. The clock folder is the core application. The clock_web folder is the web application.

Core Application

The clock application is just one file, the application. The application file defines the clock application. There is a supervisor, which is responsible for all of your fault tolerance. If any process fails for any reason, the supervisor will restart it. The entire application is under the supervision tree.

Web Application

The web application has a Router file that contains the two LiveViews. The first LiveView is the main page of the application, while the second LiveView is the live dashboard.

Start the Application

We can open the Phoenix server in Interactive Elixir mode with the command iex -S mix phx.server. This allows you to interact with Elixir while the application is running. Our phx.new generator script generated a LiveView page for us. There is a search feature on this page to search for the dependencies within the clock application. If you search for a dependency, it will direct you right to HexDocs for that library. The dependencies are referenced in the mix.exs file.

LiveView Dashboard

The generator also generated a dashboard. This is an interactive way to conceptualize your application. The dashboard shows versions, uptime, total queues, memory usage and all the processes currently running for your application. One of Elixir’s strengths is in it’s process handling. On my laptop, it can handle 250,000 processes on this application. All of these processes can be monitored from within the dashboard.

Life Cycles

LiveView has lifecycle events much like React. The first lifecycle is mount. Mount will be called when the page first renders and again by the websocket when it is connected. This is done so that the site responds with the entire page instead of waiting for additional load time like a javascript framework. If you open your network tab in your browser, you will see the WebSocket traffic. The LiveView code is found in lib/clock_web/live/. The template can be found in the same folder as the leex file extension.

Elixir Processes

Our page LiveView is an Elixir process. Think of an Elixir process as a post box that manages state and sends and receives events to mutate that state. For example, if we change the attribute query in the mount to ‘jason’, then the web page will start with the form field pre-filled out with ‘jason’.

Rewriting the Application

Now that we understand our generated project, we can begin to rewrite it. Let’s simplify the template and update the title to Clock!. We will then add an attribute to the socket to pass over an amount of seconds for the clock to run. {:ok, socket |> assign(:seconds, 15)} We can then add a button.

 

Template:

<section class=”phx-hero”>

  <h1><%= gettext “Clock!”, name: “Phoenix” %></h1>

  <div><%= @seconds %></div>

  <button>Start</button>

</section>

 

LiveView:
defmodule ClockWeb.PageLive do

  use ClockWeb, :live_view

 

  def mount(_params, _session, socket) do

    {:ok, socket |> assign(:seconds, 15)}

  end

end

Counting Down

At this point, the seconds are set to 15, but it doesn’t actually count down. We need the web page to send a message to the LiveView to start the clock. We do this with the phx-click binding. This event is handled in our LiveView with the handle_event method. Then, we insert a timer with send_interval. This will send an event to itself with the message tick. We will write a handler with the method handle_info.

 

Template:

<section class=”phx-hero”>

  <h1><%= gettext “Clock!”, name: “Phoenix” %></h1>

  <div><%= @seconds %></div>

  <button phx-click=”start-clock”>Start</button>

</section>

 

LiveView:

defmodule ClockWeb.PageLive do

  use ClockWeb, :live_view

 

  def mount(_params, _session, socket) do

    {:ok, socket |> assign(:seconds, 0)}

  end

 

  def handle_event(“start_clock”, _params, socket) do

    :timer.send_interval(1000, self(), :tick)

    {:noreply, socket |> assign(:seconds, 15)}

  end

 

  def handle_info(:tick, %{assigns: %{seconds: seconds}} = socket) do

    {:noreply, socket |> assign(:seconds, seconds – 1)}

  end

end

 

Endless Ticking

Now, the WebSocket will send a prompt to the LiveView every second to count down one second. However, we didn’t specify that the clock should stop at zero so if we let it run, it will continue going into negative numbers. Set an additional handle socket that stops the countdown when it reaches zero seconds. This is done with pattern matching. In Elixir, we can define the same function multiple times with different parameters and the first one that matches will be run.

 

def handle_info(:tick, %{assigns: %{seconds: 0}} = socket) do

  {:noreply, socket |> assign(:seconds, 0)}

end

def handle_info(:tick, %{assigns: %{seconds: seconds}} = socket) do

  {:noreply, socket |> assign(:seconds, seconds – 1)}

end

 

Repeated Ticking

The next problem is that when we click the button, we are setting another interval. That means that if we click it multiple times it will start doing multiple ticks per second. We can fix this by moving the set_interval to the mount so only one is run.

 

defmodule ClockWeb.PageLive do

  use ClockWeb, :live_view

 

  def mount(_params, _session, socket) do

    if connected?(socket), do: :timer.send_interval(1000, self(), :tick)

    {:ok, socket |> assign(:seconds, 0)}

  end

 

  def handle_event(“start_clock”, _params, socket) do

    {:noreply, socket |> assign(:seconds, 15)}

  end

 

  def handle_info(:tick, %{assigns: %{seconds: 0}} = socket) do

    {:noreply, socket |> assign(:seconds, 0)}

  end

  def handle_info(:tick, %{assigns: %{seconds: seconds}} = socket) do

    {:noreply, socket |> assign(:seconds, seconds – 1)}

  end

end

Conclusion

Elixir LiveView allows developers to iterate quickly in a safe and reliable environment. While all of these problems and solutions seem small on this scale, they are things that become more important as your project continues to grow. 

For more information on Elixir and Phoenix, check out some of our other Elixir posts

Leave a Reply

Your email address will not be published. Required fields are marked