Skip to content

How does a page get its data?

There are exactly three ways data reaches a page. Knowing which is which stops the "0 rows, no error" confusion.

1. The grid — via metadata.dataSource

A core.grid block has no data configuration on it. It renders whatever the page's top-level metadata.dataSource resolves to:

page.metadata.dataSource  ──▶  a metadata/provider/*.json whose `name` matches
                          ──▶  that provider's basePath (e.g. /api/v1/entities/oeq_equipment/records)
                          ──▶  the Entity Engine serves rows at basePath + /query

The grid's externalFilter binding supplies query filters; refreshTrigger re-runs the query. This is the only mechanism for core.grid. See Wire a page's data.

2. Page state — via action chains

Everything that isn't the grid — KPI numbers, a detail panel, a form's current values, dialog open/closed flags — lives in page state, a bag of key/value pairs. You:

  • write it with setValue actions: setValue page.kpiTotal = ${kpi.results.total.value}
  • read it with bindings: { "source": "binding", "binding": { "scope": "page", "key": "kpiTotal" } }

An action chain is an ordered list of actions on a block event (mounted, clicked, committed, rowClicked). The typical loader chain:

mounted:
  callApi  POST /api/v1/data-services/oeq-equipment-kpis/execute   → output "kpi"
  setValue page.kpiTotal = ${kpi.results.total.value}
  setValue page.kpiAvailable = ${kpi.results.available.value}

${kpi....} references the output name of an earlier callApi in the same chain. ${event.new} (in a committed handler) is the value the user entered. ${event.id} (in rowClicked) is the clicked row's id.

3. A lookup field — via optionsSourceKey

A core.lookup block calls a search Data Service named by its optionsSourceKey, passing the user's keystrokes as the search parameter, and expects back { value, label } rows. Its committed event fires with event.new = the picked record's id. See the lookup recipe.

Data Service response shapes (the part everyone gets wrong once)

operation/execute returnsbind
count{ "value": 12 }${out.value}
search / get{ "items": [...] } (or { "records": [...] } for a dataView source)${out.items}
composite{ "results": { "total": { "value": 12 }, ... } }${out.results.total.value}

A parameterized Data Service is called with args nested one level deeper: { "params": { "parameters": { "search": "lap" } } }.

Putting it together — the tutorial's Equipment Register page

mount loader block  → callApi oeq-equipment-kpis → setValue page.kpiTotal / kpiAvailable / kpiAssigned
KPI cards           → bind page.kpiTotal / ...
search + status inputs → committed → setValue page.gridExternalFilter.search / .status
grid                → page.metadata.dataSource = "oeq-equipment-provider"; externalFilter = page.gridExternalFilter
"New" button        → setValue page.form* = null/defaults; setValue page.formDialogOpen = true
dialog inputs       → committed → setValue page.form<Field> = ${event.new}
"Save" button       → callApi POST /records; setValue page.formDialogOpen=false; setValue page.gridRefreshTick=${!page.gridRefreshTick}