Documentation
Bind
Surgical, declarative data binding from signals straight into the DOM.
Bind is the surgical alternative to re-rendering a subtree: project a domain StateSignal into a flat "view model" of presentation primitives, then bind DOM text, values, and attributes to it directly via HTML attributes. No Virtual DOM diffing — each binding updates exactly the node it targets.
View models
Register a namespace once, mapping your domain signal's value into whatever flat shape the view actually needs:
import {setupBindDirectives, service_binding, createStateSignal} from '@alwatr/flux';
// Registers bind_text / bind_value / bind_attrib / bind_css_var — call once at bootstrap.
// The boolean controls whether bootstrapDirectives() runs automatically afterward.
setupBindDirectives(false);
const userSignal = createStateSignal({
name: 'user',
initialValue: {firstName: 'Ali', lastName: 'M', cart: [] as unknown[]},
});
service_binding.createViewModel('user', userSignal, (u) => ({
fullName: `${u.firstName} ${u.lastName}`,
cartIsEmpty: u.cart.length === 0,
}));setupBindDirectives() takes a required boolean — setupBindDirectives(false) if you call bootstrapDirectives() yourself afterward (the usual case, alongside the rest of your Flux setup), true if you want it to bootstrap immediately.
The bind directives
| Attribute | Behavior |
|---|---|
bind_text="ns.prop" | Sets textContent; a nullish value renders as an empty string. |
bind_value="ns.prop" | Sets an input's .value — write-guarded (skips the write if the DOM value already matches) so the caret position survives re-binding while typing. |
bind_attrib="a=[!]ns.p; b=[!]ns.p" | Toggles boolean attribute presence; a ! prefix negates. A nullish value removes the attribute; any other value is stringified. |
bind_css_var="--var: ns.prop" | Sets a CSS custom property on the element; a nullish value removes it. |
<h2 bind_text="user.fullName">Loading...</h2>
<input type="text" bind_value="user.firstName" on-input="ui_edit_name:$value" />
<button bind_attrib="disabled=user.cartIsEmpty">Checkout</button>
<div bind_css_var="--player-progress: user.progress"></div>lazy_bind
Add the lazy_bind flag attribute to defer a binding's subscription and first update until the element enters the viewport — the same IntersectionObserver machinery directives use for lazyInit_().
<div bind_text="user.fullName" lazy_bind></div>The registry
service_binding owns every view model namespace:
.createViewModel(namespace, sourceSignal, project)— registers a namespace, backed by aComputedSignalinternally..getViewModel(namespace)— returns the underlying computed signal, ornullif unregistered..removeViewModel(namespace)— destroys the computed signal and unregisters the namespace.