Skip to content

Nested and repeating component schema

Team Forms distinguishes layout components from data-nesting components. Panels, fieldsets, wells, tabs, columns, and tables arrange children without adding a response-data path. Containers, Data Grids, Edit Grids, Data Maps, and Approvals change how their child values are stored.

Parent componentChild schema locationChild response path
panel, well, fieldsetcomponentsUnchanged
tabsEach tab object’s componentsUnchanged
columnsEach column object’s componentsUnchanged
tableEach cell object’s componentsUnchanged
containercomponentsNested under the container key
datagrid, editgridcomponentsNested in each row object under the grid key
datamapvalueComponentStored under user-defined map keys
approvalcomponentsNested under the approval key

For example, a requesterEmail field inside a Panel remains data.requesterEmail. The same field inside a Container named requester becomes data.requester.requesterEmail.

Use a Container when the response must contain a nested object. Use a Panel or Fieldset when only visual grouping is required.

{
"type": "container",
"key": "requester",
"label": "Requester",
"components": [
{
"type": "textfield",
"key": "name",
"label": "Name"
},
{
"type": "email",
"key": "email",
"label": "Email"
}
]
}

This stores:

{
"requester": {
"name": "Jane Smith",
"email": "jane@example.com"
}
}

A Data Grid stores an array of objects. Each child component defines one property of every row.

{
"type": "datagrid",
"key": "lineItems",
"label": "Line items",
"responsiveLayout": true,
"responsiveBreakpoint": "md",
"reorder": true,
"components": [
{
"type": "textfield",
"key": "description",
"label": "Description",
"validate": { "required": true }
},
{
"type": "number",
"key": "quantity",
"label": "Quantity",
"validate": { "required": true, "min": 1 }
},
{
"type": "currency",
"key": "unitPrice",
"label": "Unit price",
"validate": { "required": true, "min": 0 }
},
{
"type": "currency",
"key": "lineTotal",
"label": "Line total",
"calculateValue": "value = (Number(row.quantity) || 0) * (Number(row.unitPrice) || 0)"
}
]
}

Inside a row component, use row.quantity. From outside the grid, use data.lineItems:

value = _.sumBy(data.lineItems || [], item => Number(item.lineTotal) || 0)

Omit defaultValue unless the form should start with predefined rows. To start with no rows, set initEmpty: true. To provide initial rows, use a defaultValue array whose objects match the grid’s child keys. Preserve existing row defaults when editing a form.

Useful Data Grid properties include:

PropertyBehaviour
componentsDefines the fields in every row.
reorderAllows responders to reorder rows.
addAnotherCustomises the add-row button label.
disableAddingRemovingRowsPrevents responders from changing the row count.
conditionalAddButtonJavaScript that assigns show for the add-row action.
responsiveLayoutStacks row fields on smaller screens. Use true for new grids.
responsiveBreakpointsm, md, or lg; use md by default.
freezeHeaderRowKeeps column headings visible while scrolling.

Fixed rows predefine the row labels and prevent responders from changing the row count:

{
"enableFixedRows": true,
"fixedRows": [
{ "header": "Generator", "tooltip": "Inspect the backup generator" },
{ "header": "Water pump", "tooltip": "Inspect the primary pump" }
],
"fixedRowsHeaderKey": "equipment",
"disableAddingRemovingRows": true
}

fixedRowsHeaderKey must not conflict with a child component key. Each row stores its configured header under that property.

A Data Map stores user-defined keys in an object. valueComponent defines the value editor:

{
"type": "datamap",
"key": "measurements",
"label": "Measurements",
"valueComponent": {
"type": "number",
"key": "value",
"label": "Value"
}
}

Example response:

{
"measurements": {
"temperature": 22.5,
"humidity": 48
}
}

Iterate Data Grid or Edit Grid rows with #each:

{{#each data.lineItems}}
{{@index}}. {{ this.description }}{{ this.quantity }} × {{ this.unitPrice }}
{{/each}}

Reference Container fields by their full path, for example {{ data.requester.email }}.

editgrid adds an explicit row-editing workflow, and tree stores recursive data. Preserve these components when editing existing forms, but do not generate or substantially reconfigure them without an explicit requirement and relevant existing schema context.

For nested Approval fields and dependency paths, see Approval component schema.