A repeater whose visible submitted rows are authoritative for one Lucid has-many relation. Omission is handled by the fill set and performs no write.
| Builder | F.hasMany(relation: string, schema: readonly SchemaComponent[] = []): HasMany |
| Wire type key | has-many |
| Class chain | HasMany → Repeater → Field |
| Detail projection | repeater-entry |
| Column counterpart | — (not tabulatable) |
Example
import { F } from '@adonia/core'
F.hasMany('attribute')Props
Beyond the shared field DSL:
| Prop | Declared on | Behaviour |
|---|---|---|
relation(name: string): this |
HasMany |
Changes the parent model relation used for hydration, scope, and persistence. |
keyType(type: 'number' | 'string'): this |
HasMany |
Selects the Vine/ownership id type accepted in submitted child rows. |
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 |
|---|---|---|
boundaryKeys(): readonly string[] |
HasMany |
Preserves child identity and lock snapshots across nested validation. |
contributeHints(hints: EagerHints): void |
HasMany |
Preloads the declared relation once so hydration never becomes per-row I/O. |
detailProps(): JsonObject |
HasMany |
Carries the resolved relation name into the detail projection. |
hydrateOptimisticLocks( record: unknown, value: unknown, ctx: HttpContextLike ): Promise<void> |
HasMany |
Adds the child resource’s lock snapshot after ordinary synchronous form hydration. Resource lookup is lazy and therefore must happen in this explicit asynchronous pass. |
persistRelation(record: object, value: unknown, ctx: HttpContextLike): Promise<void> |
HasMany |
Reconciles the submitted rows against children owned by the active parent. |
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
Vine base: vine.string(), vine.number().
Contributed by HasMany.vineSchema() in packages/core/src/schema/fields/has_many.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/has_many.ts—HasManypackages/core/src/schema/fields/repeater.ts—Repeater