loading... Pacem JS

<pacem-data> is the observable box of a page: park a value in its model, bind to it from anywhere, and every binding follows along. No script, no store: it's your ViewModel, written in markup.

<!-- a plain value -->
<pacem-data id="quantity" model="1"></pacem-data>

<!-- an entity, kept across reloads -->
<pacem-data id="user" model="{ name: 'Ada', age: 36 }" persist-as="my-user"></pacem-data>

Usage

Seed it. The model attribute is evaluated as JavaScript, so literals come out typed: "0" is a number, "[1, 2]" an array, "{ a: 1 }" an object. Use a binding instead and the model becomes derived: it follows whatever it's bound to.

<pacem-data id="total" model="{{ #cart.model.length }}"></pacem-data>

Read it from any binding, nested paths included: {{ #user.model.name }}.

Write it from a handler, a two-way binding or plain code:

<pacem-button on-click="#quantity.model++">+</pacem-button>
<pacem-input-text value="{{ #user.model.name, twoway }}"></pacem-input-text>

Change notifications fire when model gets assigned. Editing inside it from a handler (#user.model.name = 'Grace') changes the data, but nobody gets told: replace the model instead. Two exceptions notify on your behalf: two-way bindings on nested paths, and the mutating methods (push, splice, pop…) of an array that is the model (not of one nested in it).

<pacem-button on-click="#user.model = { ...#user.model, name: 'Grace' }">rename</pacem-button>

Persist it. persist-as="key" saves the model to local storage on every change and restores it on load. Assigning null removes the stored entry; changing the key drops the old one.

Calm it down. A chatty source (a slider, typing, a stream) can flood its dependents. debounce holds the notifications back: a number of milliseconds, or true for once per animation frame. Add throttle="true" to notify the first change of each window and drop the others.

Throttling drops, it doesn't delay: the last change of a burst may never be notified. When the final value matters, debounce.

React to it. Every notification is a propertychange event, so on-propertychange runs side effects:

<pacem-data model="{{ #search.value }}" debounce="300" on-propertychange="#results.fetch()"></pacem-data>

Examples

An entity that survives reloads

Two-way bound fields edit a persisted entity. Type something, then reload the page.

Debounce vs throttle

One source, three listeners: count how many notifications each one lets through while you type fast.