Schema

Data Binding

Understand how data flows into bands and components through binding, iteration, and nested property access.

Overview

Data binding connects your report schema to the data passed at render time. It controls which array a detail band iterates over, how each element is exposed to components, and how nested properties are accessed.

How it works

When you call renderReport(schema, data), the data object becomes the root context for expression evaluation. Every {{...}} expression in your components resolves against this context.

const data = {
  title: 'Sales Report',
  customer: { name: 'Acme Corp', city: 'San Francisco' },
  items: [
    { name: 'Widget', price: 29.99 },
    { name: 'Gadget', price: 49.99 },
  ],
}

With this data:

  • {{title}} resolves to "Sales Report"
  • {{customer.name}} resolves to "Acme Corp"
  • {{customer.city}} resolves to "San Francisco"

Detail band iteration

Detail bands require a dataBinding property that references an array in the data:

{
  type: 'detail',
  height: 28,
  dataBinding: 'items',
  components: [...]
}

The engine iterates over the array and renders the band once per element. During each iteration, the current element is available as an iterator variable.

Automatic iterator naming

The engine derives the iterator name by removing the trailing “s” from the array name:

dataBinding valueIterator variableHow you access it
itemsitem{{item.name}}
productsproduct{{product.price}}
employeesemployee{{employee.department}}
ordersorder{{order.total}}

This convention works well for standard English plurals.

Custom iteratorName

When automatic naming does not produce a useful variable name, set iteratorName explicitly:

{
  type: 'detail',
  height: 28,
  dataBinding: 'entries',
  iteratorName: 'entry',
  components: [
    {
      type: 'text',
      position: { x: 0, y: 4, width: 200, height: 20 },
      content: '{{entry.label}}',
      fontSize: 12,
    },
  ],
}

Common cases where custom naming helps:

dataBindingAuto-derivedBetter iteratorName
entriesentrieentry
datadatrow
peoplepeoplperson
addressesaddresseaddress

Nested property access

You can access deeply nested properties using dot notation in both top-level and iterator contexts:

{{company.address.street}}
{{item.metadata.category.name}}

The engine traverses the object graph following each segment. If any intermediate property is undefined or null, the expression resolves to an empty string.

Combining contexts

Inside a detail band, both the iterator variable and the top-level data context are available. Iterator variables take precedence when names collide:

const data = {
  currency: 'USD',
  taxRate: 0.08,
  items: [
    { name: 'Widget', price: 29.99 },
  ],
}

In this detail band:

  • {{item.name}} — from the iterator
  • {{currency}} — from the top-level data
  • {{formatCurrency(item.price * (1 + taxRate), currency)}} — mixed

Example: nested data

const data = {
  company: {
    name: 'Acme Corp',
    contact: {
      email: 'info@acme.com',
      phone: '+1-555-0100',
    },
  },
  orders: [
    {
      id: 'ORD-001',
      customer: { name: 'Jane Smith' },
      items: [
        { product: 'Widget', qty: 2 },
      ],
    },
  ],
}

Schema expressions:

{{company.name}}                → Acme Corp
{{company.contact.email}}       → info@acme.com
{{order.id}}                    → ORD-001 (in detail band)
{{order.customer.name}}         → Jane Smith (in detail band)

Next steps

  • Bands — band types and data binding configuration
  • Expressions — expression syntax and built-in functions
  • Render API — pass data when rendering via the API