Skip to content

Repeater ​

The repeater field repeats a small form as a list of items that can be added, sorted, duplicated and removed. On a JSON attribute it saves the items as an array, and on a relation each item is a related record.

Items ​

form defines the fields of each item, inline or as the path to a fields YAML file. prompt labels the add button, and titleFrom names an item field whose value titles the item's header. Drag the handle to sort the items, check several to drag them together, and use the item menu to duplicate, move or remove one.

yaml
fields:
    faqs:
        label: Questions
        type: repeater
        prompt: Add question
        titleFrom: question
        form:
            fields:
                question:
                    label: Question
                answer:
                    label: Answer
                    type: textarea
                    size: tiny

Each item field is named inside the repeater with the item's index, such as faqs[0][question]. The items save as a JSON array of objects, in their order on the page, and saving with no items stores null. The fields of a new item render in the create context and those of a saved item in update, so context works on item fields as it does on any form.

The attribute must hold JSON. On an October Rain model, list it in $jsonable, and on a plain Eloquent model give it an array cast. See Models for both.

php
/**
 * @var array<int, string> jsonable attributes stored as JSON for the structured fields.
 */
protected $jsonable = [
    'features',
    'permissions',
    'interests',
    'tags',
    'topics',
    'content',
    'steps',
    'address',
    'line_items',
    'meta',
    'shipping',
    'contact',
    'faqs',
    'highlights',
    'milestones',
    'dimensions',
    'sections',
    'specs',
    'itinerary',
    'languages',
    'deliveries',
    'variants',
    'stockists',
    'parts',
    'price_breaks',
    'bundle_items',
    'suppliers',
    'components',
    'stock_levels',
    'reviews',
    'spec_sheet',
    'certifications',
    'awards',
    'seo',
    'packaging',
];

Item limits ​

minItems starts the field with that many empty items, and stops removal below that count. maxItems hides the add button once the field is full. With groups, minItems adds no empty items, since the field cannot tell which group they would be.

yaml
fields:
    highlights:
        label: Highlights
        type: repeater
        prompt: Add highlight
        minItems: 2
        maxItems: 4
        form:
            fields:
                text:
                    label: Highlight
        comment: Between two and four highlights.

Collapsed items ​

itemsExpanded: false shows each item as its header only, titled by titleFrom. Opening one item closes the others.

yaml
fields:
    milestones:
        label: Milestones
        type: repeater
        prompt: Add milestone
        titleFrom: title
        itemsExpanded: false
        form:
            fields:
                title:
                    label: Title
                    span: left
                due:
                    label: Due
                    span: right

Fixed order ​

showReorder: false removes the drag handle and the move options from the item menu, so the items stay in the order they were added. showDuplicate: false removes Duplicate from the item menu.

yaml
fields:
    dimensions:
        label: Dimensions
        type: repeater
        prompt: Add dimension
        showReorder: false
        showDuplicate: false
        form:
            fields:
                label:
                    label: Label
                    span: left
                value:
                    label: Value
                    span: right

Groups ​

groups replaces form with several item types, and the add button opens a menu of them. Each group is keyed by its code and has a name, a description, an icon and its fields, plus its own titleFrom. groups also takes the path to a YAML file of groups, and a group can be the path to a YAML file of its own.

yaml
fields:
    sections:
        label: Sections
        type: repeater
        prompt: Add section
        groupKeyFrom: type
        groups:
            text:
                name: Text
                description: A title and a paragraph
                icon: ph ph-text-align-left
                titleFrom: title
                fields:
                    title:
                        label: Title
                    body:
                        label: Body
                        type: textarea
                        size: tiny
            image:
                name: Image
                description: A caption and how wide the image runs
                icon: ph ph-image
                titleFrom: caption
                fields:
                    caption:
                        label: Caption
                        span: left
                    alignment:
                        label: Width
                        type: dropdown
                        span: right
                        options:
                            inline: Inline
                            wide: Wide
                            full: Full width

Each saved item records its group's code under groupKeyFrom, which defaults to _group. Here it is type, so a text item saves as {"title": "...", "body": "...", "type": "text"}.

Builder ​

displayMode: builder lists the items in a sidebar and edits one at a time beside it, which suits longer items. A group can set useTabs for its own fields, and a group field can be another repeater.

yaml
fields:
    content:
        label: Content Blocks
        type: repeater
        displayMode: builder
        prompt: Add content block
        groups:
            heading:
                name: Heading
                description: A title with an optional subtitle
                icon: ph ph-text-h
                titleFrom: title
                fields:
                    title:
                        label: Title
                        span: left
                    subtitle:
                        label: Subtitle
                        span: right
            paragraph:
                name: Paragraph
                description: Text with its options on a second tab
                icon: ph ph-paragraph
                titleFrom: text
                useTabs: true
                fields:
                    text:
                        label: Text
                        type: textarea
                        size: small
                        tab: Content
                    align:
                        label: Alignment
                        type: radio
                        inlineOptions: true
                        tab: Options
                        default: left
                        options:
                            left: Left
                            center: Center
                            right: Right
            quote:
                name: Quote
                description: A quote and who said it
                icon: ph ph-quotes
                titleFrom: author
                fields:
                    quote:
                        label: Quote
                        type: textarea
                        size: tiny
                    author:
                        label: Author
            checklist:
                name: Checklist
                description: A nested repeater of checklist items
                icon: ph ph-list-checks
                fields:
                    items:
                        label: Items
                        type: repeater
                        prompt: Add item
                        titleFrom: label
                        form:
                            fields:
                                label:
                                    label: Item
                                    span: left
                                done:
                                    label: Done
                                    type: checkbox
                                    span: right

Tabs ​

useTabs moves the item fields into tabs, named by each field's tab, so every item has its own set of tabs.

yaml
fields:
    specs:
        label: Specifications
        type: repeater
        prompt: Add specification
        titleFrom: property
        useTabs: true
        form:
            fields:
                property:
                    label: Property
                    tab: Value
                value:
                    label: Value
                    span: left
                    tab: Value
                unit:
                    label: Unit
                    span: right
                    tab: Value
                note:
                    label: Note
                    type: textarea
                    size: tiny
                    tab: Notes

Nested repeaters ​

An item field can be another repeater. Its items save as an array inside each item, such as [{"day": "...", "stops": [{"place": "..."}]}].

yaml
fields:
    itinerary:
        label: Itinerary
        type: repeater
        prompt: Add day
        titleFrom: day
        form:
            fields:
                day:
                    label: Day
                stops:
                    label: Stops
                    type: repeater
                    prompt: Add stop
                    titleFrom: place
                    form:
                        fields:
                            place:
                                label: Place

Watching the parent form ​

Item fields are named inside the repeater, so a trigger that watches a field of the parent form starts its name with ^, one for each level up. Here every step's Details field shows while the parent form's Step Style is Detailed.

yaml
fields:
    step_style:
        label: Step Style
        type: balloon-selector
        default: simple
        options:
            simple: Simple
            detailed: Detailed
    steps:
        label: Steps
        type: repeater
        prompt: Add step
        titleFrom: title
        form:
            fields:
                title:
                    label: Title
                    span: left
                minutes:
                    label: Minutes
                    type: number
                    span: right
                    min: 0
                details:
                    label: Details
                    type: textarea
                    size: tiny
                    trigger:
                        action: show
                        field: ^step_style
                        condition: value[detailed]

Relation ​

When the field name is a relation, each item is a related record, and the item fields are its attributes. Here links is a hasMany relation to SampleLink.

yaml
fields:
    links:
        label: Links
        type: repeater
        prompt: Add link
        titleFrom: title
        form:
            fields:
                title:
                    label: Title
                    span: left
                url:
                    label: Address
                    span: right
        comment: Each item is a link record, saved through the hasMany relation.
php
/**
 * @var array<string, mixed> hasMany relations, the links the repeater edits as records.
 */
public $hasMany = [
    'links' => [SampleLink::class, 'delete' => true],
];

Adding an item creates an empty record at once, so its columns must accept empty values, and attaches it against the form's session key. Removing an item detaches it the same way. Save commits both and writes each item's fields, and when the related model uses the Sortable trait it also stores the item order. Pass the session key when you save, as Building a Form Page shows. With groups, the group code is stored in the related model's groupKeyFrom column.

php
<?php

namespace App\Models;

use Database\Factories\SampleLinkFactory;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use October\Rain\Database\Model;
use October\Rain\Database\Traits\Sortable;

/**
 * SampleLink is a link on a sample, stored as its own record so the repeater can edit a hasMany relation.
 */
#[Fillable(['title', 'url'])]
class SampleLink extends Model
{
    /** @use HasFactory<SampleLinkFactory> */
    use HasFactory;
    use Sortable;

    /**
     * @var array<string, mixed> belongsTo relations
     */
    public $belongsTo = [
        'sample' => Sample::class,
    ];
}

A plain Eloquent model has no deferred binding, so there adding and removing items writes to the database straight away.

Preview ​

disabled renders the items in preview mode, where they can only be expanded and collapsed, and the field is left out of the save. This copy reads the questions through valueFrom.

yaml
fields:
    faqs_preview:
        label: Questions
        type: repeater
        valueFrom: faqs
        disabled: true
        titleFrom: question
        form:
            fields:
                question:
                    label: Question
                answer:
                    label: Answer
                    type: textarea
                    size: tiny

Options ​

OptionDescription
formthe fields of each item, inline or as the path to a fields YAML file.
groupsseveral item types in place of form, inline or as the path to a YAML file. See below.
promptthe add button label. Default: Add New Item.
titleFromthe item field whose value titles each item. With groups, each group's own titleFrom applies instead. Default: none.
groupKeyFromthe key, or the related model's column, that stores each item's group code. Default: _group.
minItemsthe fewest items, added empty when there are fewer. Default: none.
maxItemsthe most items, after which the add button hides. Default: none.
displayModeaccordion lists the items in place, builder edits them one at a time beside a sidebar. Default: accordion.
itemsExpandedshows every item open. false shows the headers and opens one at a time. Default: true.
showReorderallows sorting the items. Default: true.
showDuplicateadds Duplicate to the item menu. Default: true.
useTabsmoves the item fields into tabs, named by each field's tab. Default: false.

The field also takes the common field properties, such as disabled and valueFrom.

Group options ​

OptionDescription
namethe group name in the add menu.
descriptiona line under the name in the add menu.
iconthe group's icon class, such as ph ph-image. Default: the icon pack's default group icon.
fieldsthe group's item fields.
titleFromthe item field whose value titles the group's items.
useTabsmoves the group's fields into tabs. Default: the field's useTabs.