Skip to content

repeater

Repeatable sub-form bound to an array attribute (§7.3).

Note the ./title in the child rule: inside an item, ./-prefixed paths are row-relative and everything else addresses the root form state.

Builder F.repeater(attribute: string, schema: readonly SchemaComponent[] = []): Repeater
Wire type key repeater
Class chain RepeaterField
Detail projection repeater-entry
Column counterpart (not tabulatable)

Example

import { F, when } from '@adonia/core'

F.repeater('items', [
  F.text('title').required(),
  F.number('qty').min(1).required(),
  F.text('note').visibleWhen(when('./title').isTruthy()),
])
  .min(1)
  .max(20)
  .reorderable()
  .collapsible()
  .itemLabel('title')
  .defaultItems(1)

Props

Beyond the shared field DSL:

Prop Declared on Behaviour
min(count: number): this Repeater Minimum number of rows; compiles to the array’s minLength (§11.1).
max(count: number): this Repeater Maximum number of rows; compiles to the array’s maxLength (§11.1).
reorderable(): this Repeater Lets the user drag rows into a new order (order is the array order).
collapsible(): this Repeater Lets the user fold individual rows away.
itemLabel(source: string | ItemLabelFn): this Repeater Names each row.
table(): this Repeater Compact layout: rows render as table lines rather than stacked cards. A rendering hint only — the state shape and the validator are identical either way.
defaultItems(count: number): this Repeater Pre-populates count blank rows in create mode (protocol §7): the node’s default becomes an array of Repeater.blankItems, so a form that always has at least one row does not make the user press “add” first.

Introspection

Read-only members this type adds — used by the compiler, the table derivation and tests rather than by a schema author.

Member Declared on Behaviour
nestedSchema(): readonly SchemaComponent[] Repeater The item sub-form (protocol §2 children of this node). Declared here rather than as a LayoutComponent’s children because this is a STATE boundary: the descriptor compiler emits the sub-form as children but stops collecting stateKeys, and collectFields never descends.
blankItem(): RepeaterItem Repeater A fresh empty row: every child key present, holding its declared default or explicit null.
labelFor(item: RepeaterItem, index: number): string Repeater The human name of one row, server-side: the itemLabel closure when declared, otherwise the named child key’s value, otherwise the ordinal.

Validation

§11.1 mapping: vine.array(vine.object(childSchema)).minLength(min) .maxLength(max).

The item object comes from buildFieldObjectSchema — the very function that compiles the resource’s own validator — so each child contributes its OWN buildValidation and the §11.2 conditional-group machinery (prune, bucketed vine.group, native requiredWhen) applies inside an item exactly as it does at the top level. scoped tells those group predicates that Vine hands them the ITEM, which is the ./ row scope.

Recursion is free: a child repeater’s vineSchema runs the same path, which is why items.0.children.1.name validates and reports correctly without a single nesting-aware branch anywhere.

Contributed by Repeater.vineSchema() in packages/core/src/schema/fields/repeater.ts. The shared modifiers (required, nullable, requiredWhen, unique, optionality) are applied on top by Field.buildValidation, and a rules() override replaces the base entirely — the validator compiler never branches on the field class.

Declared in

  • packages/core/src/schema/fields/repeater.tsRepeater
Navigation

Type to search…

↑↓ navigate↵ selectEsc close