Template and handler helpers imported by use Dstar.Page.
event/1,2— Datastar action expression targeting the page's own_eventroute, resolved client-side vialocation.pathname.connect/0,1— Datastar action expression opening the page's SSE stream.patch/3,4— render a function component into apatch_elementscall.
Summary
Functions
Builds the stream-connect expression for data-init /
data-on:online__window.
Builds a page-local Datastar action expression.
Wires a container to re-run action whenever that nudge fires.
Renders a function component and pipes it to Dstar.Elements.patch/3.
Functions
Builds the stream-connect expression for data-init /
data-on:online__window.
connect()
#=> "@post(location.pathname, {retryMaxCount: Infinity})"
connect(include_search: true)
#=> "@post(location.pathname + location.search, {retryMaxCount: Infinity})"Options
:opts— override the options object (default"{retryMaxCount: Infinity}"):include_search— appendlocation.searchso query params reachhandle_connect(pages whose render depends on them, e.g.?step=).
Always emits @post — Dstar streams connect over POST.
retryMaxCount does not bound reconnect cycles
It counts consecutive failures to connect, and the client resets it —
along with the backoff interval — on every 200. So it cannot cap a loop
that reconnects successfully each time round: the budget never
accumulates, whatever finite value you set. Only retryMaxCount: 0
stops such a loop.
This applies when the stream ends as a transport error: an HTTP/2
takeover (the stream process is killed outright rather than halting),
the registry's kill escalation, or a crash. A stream that ends
cleanly — the ordinary HTTP/1.1 takeover, where the loop halts and
the response is terminated properly — does not reconnect at all under
the default retry: "auto"; reconnecting after a clean end requires
retry: "always".
If you combine auto-reconnect with Dstar.Utility.StreamRegistry dedup
on a stack where takeovers end as errors, cap it:
connect(opts: "{retryMaxCount: 0}")Builds a page-local Datastar action expression.
event("increment")
#=> "@post(location.pathname.replace(/\/+$/, '') + '/_event/increment')"
event("remove", verb: :delete)
#=> "@delete(location.pathname.replace(/\/+$/, '') + '/_event/remove')"The URL is computed in the browser, so path params (workspace slugs,
ids) need no server-side threading. Event names become a single URL
path segment: they must not contain / or '.
Trailing slashes in the path are stripped client-side so pages mounted at "/" or visited with a trailing slash don't produce protocol-relative ("//") or double-slash URLs.
Options
:verb—:get | :post | :put | :patch | :delete(default:post):opts— raw JS object string appended as the action's options, e.g."{indicator: 'saving'}"
Wires a container to re-run action whenever that nudge fires.
<div id="posts" {on_nudge("posts", event("reload"))}>
Renders a function component and pipes it to Dstar.Elements.patch/3.
conn |> patch(&history/1, value: count)
conn |> patch(&item_card/1, [item: item], selector: "#row-1", mode: :outer)With no :selector, Datastar matches elements by their id attribute,
so the component's root element must carry one.