Data binding
An extension for pages that render external/dynamic data declaratively, without domain logic on the client. By default a service is server-driven (the endpoint returns a ready contract). Binding is needed when the source is third-party JSON and you have no proxy of your own.
Source
The page declares a URL that is fetched (GET) before rendering; the response is
available in interpolation as data:
"page": {
"source": { "url": "https://{settings.domain}/api/v2/summary.json" },
"items": [ … ]
}
source can be an array of steps — a chain. The next step’s URL may reference
the previous step’s data (data.<as>):
"source": [
{
"as": "geo",
"url": "…/search?name={settings.city}&count=1"
},
{
"as": "weather",
"url": "…/forecast?latitude={data.geo.results.0.latitude}&longitude={data.geo.results.0.longitude}¤t=temperature_2m"
}
]
Interpolation {path | transform:arg | …}
Works in any string field. Paths are dot-separated, with indices:
{data.status.description}, {data.components.0.name}. Unresolved → empty string.
Scopes:
| Scope | What |
|---|---|
data | the source body (in a chain — data.<as>.…) |
settings | persisted fields (persist: true): {settings.domain} |
context | the current item inside a list section |
List section (template + data)
data — a path to an array (or an inline array). For each item (with an optional
where filter) template is rendered; the item is substituted as {context.*}:
{
"type": "section",
"header": "Components",
"data": "data.components",
"where": {
"group": false
},
"template": {
"type": "row",
"title": "{context.name}",
"status": "{context.status | map:compRole}",
"wrap": true
}
}
zip — stitching parallel columns
For APIs that return columns rather than an array of objects. The result length =
the shortest column; each object is available as {context.<key>}:
{
"type": "section",
"header": "7-day forecast",
"data": {
"zip": {
"day": "data.daily.time",
"max": "data.daily.temperature_2m_max"
}
},
"template": {
"type": "row",
"title": "{context.day | date:EEEE}",
"value": "{context.max | round:0}°"
}
}
Transforms (pipeline via |)
| transform | result |
|---|---|
date / date:short/medium/long | ISO8601 (and local forms) → localized date+time |
date:<pattern> | explicit Unicode pattern (yyyy-MM-dd, HH:mm, dd MMM yyyy HH:mm) |
map:<table> | via a table from page.maps (key _default — fallback) |
number | grouping by locale |
round:<N> | N digits after the decimal point |
percent[:N] | fraction (0..1) → “12%“ |
bytes | bytes → KB/MB/… |
default:<x> | empty/nil → x |
map tables
"maps": {
"compRole": {
"operational": "success",
"major_outage": "error",
"_default": "neutral"
}
}
Security
source is fetched strictly from the
manifest origin (same-origin, with authorization). An absolute cross-origin
URL is rejected — no exceptions. If you need third-party data, proxy it through your
own origin.