Views and htmx
Anvil’s own views follow these rules; use them in yours.
Token and action. Send _token and _action as hidden inputs of a form
or with hx-vals on the element that sends the request (a button):
<form hx-post="<?= esc($endpoint, 'attr') ?>" hx-target="#settings" hx-swap="outerHTML" hx-disinherit="*" hx-disabled-elt="find button[type=submit]"> <input type="hidden" name="_token" value="<?= esc($token, 'attr') ?>"> <input type="hidden" name="_action" value="save"> …</form>Never put the token into hx-vals of a container (div, form): hx-vals
is inherited, and a boosted link inside would put the token into the URL.
Target and swap.
| Pattern | Use |
|---|---|
hx-target="#id" hx-swap="outerHTML" |
the answer is the whole component (as in the example) |
hx-target="#id" hx-swap="innerHTML" |
the answer is the content of a container, e.g. a dropdown menu |
hx-target="#id" hx-select="#id" hx-swap="outerHTML" |
the answer is larger than the part to replace (Anvil’s lists) |
hx-swap="none" |
the answer only carries events, e.g. ComponentResponse::empty()->withEvent(…) |
hx-swap-oob="true" on an element of the answer |
updates a second part of the page in the same answer (the widgets above a list) |
No inheritance. A form or container with hx-target/hx-swap passes
them on to every link and button inside. Set hx-disinherit="*" (or
hx-disinherit="hx-target hx-select hx-swap") on it.
Other attributes Anvil uses.
hx-disabled-elt="find button[type=submit]": disables the submit button while the request runs.hx-sync="closest form:replace"orhx-sync="this:replace": a new request replaces a running one (search inputs).hx-trigger="input changed delay:400ms, search": search while typing.hx-params="_token,_action,q": sends only these fields, e.g. a search field inside a larger form.hx-confirm="…": confirmation in Anvil’s dialog.- Links to downloads need
hx-boost="false"(the body is boosted).
Forms in a component
Section titled “Forms in a component”On a 422 answer, anvil.js shows the messages next to the fields of the
form that sent the request. It finds a field by an element with
data-anvil-field="<input name>" and writes the message into its
[data-anvil-error] element; errors without a matching field are shown as
a toast. Other errors (403, 404, 429, network) are shown as a toast.
<div class="anvil-field mb-2" data-anvil-field="comment"> <label class="form-label" for="comment">Comment</label> <textarea class="form-control" id="comment" name="comment" rows="2" maxlength="2000"></textarea> <div class="invalid-feedback d-block d-none" data-anvil-error></div></div>novalidate on the form leaves validation to the server. Anvil’s field
views render this markup already (see Forms).