I think this is one of those things that seems simple, but starts getting complicated once you dig into it (especially as a beginner).
As mentioned, you can have a stateful process running on your server using a GenServer:
defmodule Clock do
use GenServer
def new_clock() do
{:ok, pid} = GenServer.start_link(Clock, [])
pid
end
def get_current_time(pid) do
:sys.get_state(pid)
end
# Server callbacks
@impl true
def init(_init_arg) do
# schedule the first `tick` event in 1 second (1000 milliseconds)
Process.send_after(self(), :tick, 1000)
# set initial state
{:ok, DateTime.utc_now(:second)}
end
def handle_info(:tick, state) do
# each `tick` event schedules the next one
Process.send_after(self(), :tick, 1000)
{:noreply, DateTime.add(state, 1, :second)}
end
end
Then, you could use the clock like this:
# make a new clock
clock_pid = Clock.new_clock()
# get the current time
Clock.get_current_time(clock_pid)
You could then hook that into your LiveView and send periodic updates to the client (I donāt have time to remember how to implement that part since I havenāt touched LiveView in a while
)
If you are making a web app, you could probably just use Javascript for this instead of making the server do all the work. Alpine.js is a great tool to easily to add interactivity to websites. Hereās an example to get you started.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Hello Alpine.js!</title>
<!-- js -->
<script
defer
src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.js"
></script>
</head>
<body>
<h1>Hello Alpine.js!</h1>
<div
data-time="1719168461000"
x-data="{ time: Number($el.dataset.time) }"
x-init="setInterval(() => { time = time + 1000}, 1000)"
x-text="new Date(time)"
>
</div>
</body>
</html>
If you needed to pass an initial value to the HTML from the server, you could just do something like data-time="<%= @time %>"
to get the server-rendered data into your template, then have Javascript do the work on the client side from that point forward.