Cross-row aggregates
A repeater’s value is a plain array of row objects. That means a form-level
expression can reference the whole table with %fieldName% and hand it to an
array-aware function:
sum(%directors%, 'sharePct')No new syntax — these are ordinary function calls, dispatched through the expression function registry.
Built-in aggregates
Section titled “Built-in aggregates”| Call | Returns |
|---|---|
sum(rows, 'col') | Total of the numeric values in col. |
count(rows) | Number of rows. |
count(rows, 'col') | Rows where col is filled. |
min(rows, 'col') | Smallest numeric value, or null if there are none. |
max(rows, 'col') | Largest numeric value, or null if there are none. |
avg(rows, 'col') | Mean of the numeric values, or null if there are none. |
The column argument is optional. Omit it when the array holds primitives rather
than row objects: sum(%scores%).
Behaviour with half-typed data
Section titled “Behaviour with half-typed data”These run on every keystroke, against forms that are mostly empty most of the time, so the edge cases are deliberate:
- Non-numeric and empty cells contribute
0tosum. One half-typed row must not turn a running total intoNaN. min,maxandavgreturnnullwhen no numeric values exist — not0, which would be indistinguishable from a real zero in the data.- A non-array aggregates to the empty case rather than throwing. A repeater
that hasn’t been seeded yet, or was cleared to
'', is not an error. count(rows, 'col')skips empty strings,null,undefinedand empty arrays — “filled”, not merely “present”.
Feeding a calculated field
Section titled “Feeding a calculated field”The common case: a total that follows the rows.
Editing a row changes the array’s identity, which the calculated-value graph sees as a dependency change — so the total settles synchronously, in the same write. See calculated values.
Driving show and required
Section titled “Driving show and required”Aggregates aren’t limited to calculateValue. They work anywhere an expression
does, which is how you express whole-table rules:
{ "name": "overAllocated", "type": "error", "required": "sum(%directors%, 'sharePct') > 100", "validation": [ { "name": "required", "message": "Shares add up to more than 100%." } ]}An error field holds no value of its own — it exists to carry a group rule and
render the message when it fails. Pairing an aggregate required expression
with a required validator gives you “these rows must add up” without writing a
custom validator.
The same works for visibility:
{ "name": "bulkDiscountNote", "type": "text", "label": "Bulk discount applied", "show": "count(%items%) >= 10"}Writing your own
Section titled “Writing your own”median, stddev, a weighted average — register it once and it’s callable from
any expression. See
expression functions.