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 and path behaviour
Section titled “Parent and path behaviour”| Parent component | Child schema location | Child response path |
|---|---|---|
panel, well, fieldset | components | Unchanged |
tabs | Each tab object’s components | Unchanged |
columns | Each column object’s components | Unchanged |
table | Each cell object’s components | Unchanged |
container | components | Nested under the container key |
datagrid, editgrid | components | Nested in each row object under the grid key |
datamap | valueComponent | Stored under user-defined map keys |
approval | components | Nested 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.
Container
Section titled “Container”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" }}Data Grid
Section titled “Data Grid”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:
| Property | Behaviour |
|---|---|
components | Defines the fields in every row. |
reorder | Allows responders to reorder rows. |
addAnother | Customises the add-row button label. |
disableAddingRemovingRows | Prevents responders from changing the row count. |
conditionalAddButton | JavaScript that assigns show for the add-row action. |
responsiveLayout | Stacks row fields on smaller screens. Use true for new grids. |
responsiveBreakpoint | sm, md, or lg; use md by default. |
freezeHeaderRow | Keeps column headings visible while scrolling. |
Fixed rows
Section titled “Fixed rows”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.
Data Map
Section titled “Data Map”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 }}Handlebars email templates
Section titled “Handlebars email templates”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 }}.
Advanced nested components
Section titled “Advanced nested components”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.