How PAD answers htmx
htmx asks for HTML and swaps it in. A PAD page is HTML with names on its parts - so the same template answers the browser's first visit and every question htmx asks after it.
1. Name the part
A {fragment} around the part that changes; the element around it gets the same name as its id:
<input name="q" hx-get="?products" hx-trigger="input changed delay:300ms" hx-target="#results">
<ul id="results">
{fragment 'results'}
{products}<li>{$name}</li>{/products}
{/fragment}
</ul>
2. PAD answers the part alone
htmx sends HX-Request: true and the id of the element it swaps in
HX-Target. When the page has a fragment of that name, the answer is that fragment
and nothing else - no wrapper, no rest of the page; when it has none the page renders whole,
as it always did. A request can also name the fragment itself, for an element without an id
or another part than the target:
<tr hx-get="?orders&p=2&padFragment=rows" hx-trigger="revealed" hx-swap="afterend">
The page's PHP runs as for any request - $q is the request's value - and may narrow
what the fragment shows: $padFragmentOnly = 'row' sets the answer from PHP. A
fragment inside a loop answers with the first row it renders, so narrow the rows to the one
asked for.
3. Posts carry the token
With $padCsrf on, a post without the session's token is refused before the page
runs. A {form} carries it as a field; for an hx-post on anything else, put
the header on the body once - the ^ sigil writes the array as JSON for the attribute:
<body hx-headers="{^headers}"> <!-- $headers = [ 'X-CSRF-Token' => padCsrfToken () ] -->
4. Forms check the same rules twice
Fields with rules= are checked on the server before the PHP runs, and with
client in the browser first. Posted through htmx, a form that broke a rule comes
back as its own fragment, refilled, with PAD's messages beside the fields:
<div id="signup">{fragment 'signup'}
{form 'signup', client, error, hx-post='?signup', hx-target='#signup'}
{input 'email', label='E-mail', rules='required|email'}
{/form}
{/fragment}</div>
5. More than one place
An answer can update other parts of the page as well: elements in it marked
hx-swap-oob="true" go where their id is - the cart count in the header, a summary
beside the list.
6. Pushed by the server
padSse() keeps a page's connection open and sends events; with htmx's sse
extension each event's HTML is swapped in as it arrives:
<div hx-ext="sse" sse-connect="?ticker" sse-close="done">
<ol sse-swap="tick" hx-swap="afterbegin"></ol>
</div>
The page cache knows htmx: a request for a part is answered fresh and never stored, and a fragment chosen by a header is never stored under the page's address.