How To Build a [Counter with Elixir, Phoenix, LiveView and Tailwind CSS] | No Javascript
Learn how to use Elixir/Phoenix, to create a counter without javascript, only using Elixir with the help of Phoenix LiveView, and Tailwind CSS for the user interface.
Step 1:
Let's create a fresh elixir/phoenix new project by going to the command line and typing the following in your desired directory:
$ mix phx.new counter --no-ecto --no-gettext --no-mailer --no-dashboard && cd counter
Step 2:
Let's add and configure Tailwind CSS, go to the website hex.pm and search for the tailwind library to grab the latest version, so you can copy and that to your mix config file, at the time of the making of this tutorial the latest version is 0.1.5, add the following to your project dependencies inside your mix.exs
file:
{:tailwind, "~> 0.1.5"}
Then in the command line type in $ mix deps.get && mix tailwind.install
to fetch your new dependency and install tailwind. Back in your mix.exs
file, under aliases, add the following:
"assets.deploy": ["tailwind default --minify", ..., "phx.digest"]
Go to your config/config/exs
file and add the following configuration:
config :tailwind,
version: "3.0.10",
default: [
args: ~w(
--config=tailwind.config.js
--input=css/app.css
--output=../priv/static/assets/app.css
),
cd: Path.expand("../assets", __DIR__)
]
Go to your config/dev.exs
file and add the following to your watchers
:
tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]}
Step 3:
Add the following folder under test/counter_web
:
live
Add the following file to that newly created folder:
page_live_test.exs
To that newly created file test/counter_web/live/page_live_test.exs
add the following tests:
defmodule CounterWeb.PageLiveTest do
use CounterWeb.ConnCase
import Phoenix.LiveViewTest
test "disconnected and connected render", %{conn: conn} do
{:ok, page_live, disconnected_html} = live(conn, "/")
assert disconnected_html =~ "0"
assert render(page_live) =~ "0"
end
test "increment event and decrement", %{conn: conn} do
{:ok, page_live, _html} = live(conn, "/")
assert render_click(page_live, :inc, %{}) =~ "1"
assert render_click(page_live, :inc, %{}) =~ "2"
assert render_click(page_live, :inc, %{}) =~ "3"
assert render_click(page_live, :dec, %{}) =~ "2"
assert render_click(page_live, :dec, %{}) =~ "1"
assert render_click(page_live, :dec, %{}) =~ "0"
end
test "clear event", %{conn: conn} do
{:ok, page_live, _html} = live(conn, "/")
assert render_click(page_live, :inc, %{}) =~ "1"
assert render_click(page_live, :clear, %{}) =~ "0"
end
end
If you run those tests by going to the command line and typing mix test test/counter_web/live
they should fail of course.
Step 4:
Open your lib/counter_web/router.ex
file and remove the root path and change it to the following:
scope "/", CounterWeb do
pipe_through :browser
live "/", PageLive
end
Run the server with the following command in your terminal:
$ iex -S mix phx.server
Open your lib/counter_web/templates/layout/root.html.heex
and remove the header nav, your html body tag should look like the following:
<body>
<%= @inner_content %>
</body>
To your lib/counter_web/templates/layout/live.html.heex
file, add the following css class to your main tag:
<main class="container mx-auto max-w-full">
Add the following folder to lib/counter_web
:
live
Add the following file to that newly created live
folder:
page_live.ex
Add the following to that newly created file lib/counter_web/live/page_live.ex
:
defmodule CounterWeb.PageLive do
use CounterWeb, :live_view
end
Under lib/counter_web/live
add the following file:
page_live.html.heex
Add the following to that newly created lib/counter_web/live/page_live_html.heex
file:
<div class="flex flex-col items-center justify-center h-screen bg-gray-200">
<h1 class="text-5xl font-medium text-gray-700">Counter</h1>
<span class="m-5 text-9xl">
0
</span>
<section>
<button class="text-white text-4xl bg-indigo-600 px-6 py-4 rounded hover:bg-indigo-900">-</button>
<button class="m-10 text-white text-4xl bg-indigo-900 px-6 py-4 rounded hover:bg-indigo-500">clear</button>
<button class="text-white text-4xl bg-indigo-600 px-6 py-4 rounded hover:bg-indigo-900">+</button>
</section>
</div>
Step 5:
Let's add our liveview mount function and assign the default number.
To lib/counter_web/live/page_live.ex
file add the following function:
def mount(_params, _session, socket) do
{:ok,
socket
|> assign(number: 0)}
end
Then change the number 0 to our new assign variable in our lib/counter_web/live/page_lve.html.heex
file, inside our span tag:
<span class="m-5 text-9xl">
<%= @number %>
</span>
Step 6:
Inside our lib/counter_web/live/page_lve.html.heex
file, add the following click event binding to our increase button:
<button phx-click="inc" class="...">+</button>
Then to our lib/counter_web/live/page_live.ex
file let's add the following function to handle that event:
def handle_event("inc", _params, socket) do
{:noreply,
socket
|> update(:number, &(&1 + 1))}
end
Step 7:
Inside our lib/counter_web/live/page_lve.html.heex
file, add the following click event binding to our decrease button:
<button phx-click="dec" class="...">-</button>
To our lib/counter_web/live/page_live.ex
file let's add the following function to handle the decrease event:
def handle_event("dec", _params, socket) do
{:noreply,
socket
|> update(:number, &max(0, &1 - 1))}
end
Step 8:
Let's add the following click event binding to our clear button inside our lib/counter_web/live/page_lve.html.heex
file:
<button phx-click="clear" class="...">clear</button>
To our lib/counter_web/live/page_live.ex
file let's add the following function to handle the clear event:
def handle_event("clear", _params, socket) do
{:noreply, socket |> assign(number: 0)}
end
Go to localhost:4000 to test your application manually as a user, and if you go to the command line and run the tests with mix test test/counter_web/live
they should pass with no failure.
Conclusion
That's all folks, we went through the simple steps to set up Tailwind CSS with your phoenix applications, and how to handle click events with Phoenix LiveView over sockets without the need of using Javascript code, the Javascript is handled nicely in the background for us by the LiveView library. Thank you so much for your time, I really appreciate it.