Signals lifecycle
Inspect the signals handed to handle_event/3, patch server-owned values, and remove client signals while watching the current flow.
Demo
Signal snapshot
mounted
- scope
- release
- filter
- open
- ephemeral
- "client-note"
- server_tick
- 0
{}
How it works
- handle_event/3 receives the request signals already read.
- patch_signals/3 updates server-owned signals.
- remove_signals/3 clears selected client state.
- The inspector itself is patched after each command.
Signals are client state. Datastar posts the current set with every event, so
handle_event/3
receives them already read; patch_signals/2
writes server-owned values back and remove_signals/2
deletes client ones. The inspector prints the raw map it was handed, minus the CSRF and
tab keys.
def handle_event(conn, "patch", signals) do
tick = next_tick(signals)
state = client_state(signals, tick: tick, audit: "patch_signals tick #{tick}")
conn
|> patch_signals(state)
|> patch(&inspector/1, state)
end
If this were LiveView
This is the one comparison where the two sides are not solving the same problem, because LiveView does not have the concept this page inspects.
Dstar
def handle_event(conn, "patch", signals) do
tick = next_tick(signals)
state = client_state(signals, tick: tick, audit: "patch_signals tick #{tick}")
conn
|> patch_signals(state)
|> patch(&inspector/1, state)
end
LiveView
# Illustrative LiveView equivalent — not compiled or run.
#
# There is no client-owned state to inspect: assigns live on the server,
# and the client only ever sends what a binding names explicitly. So this
# page has no real LiveView counterpart — only the nearest thing.
def mount(_params, _session, socket) do
{:ok,
assign(socket,
scope: "release",
filter: "open",
server_tick: 0,
audit_trail: "mount"
)}
end
def handle_event("patch", _params, socket) do
tick = socket.assigns.server_tick + 1
{:noreply, assign(socket, server_tick: tick, audit_trail: "assign tick")}
end
# To send client-held values at all, each one must be named:
#
# <button phx-click="patch" phx-value-scope={@scope} phx-value-filter={@filter}>
#
# or a JS hook pushes them:
#
# this.pushEvent("patch", {scope, filter})