Skip to content

Repeater

A repeater is form layout that allows you to repeat certain fields at a user's request. This allows you to easily create records that belong to a one-to-many relationship, or an embedded array of structs in your underlying model.

Getting Started

You can create a new repeater as so:

go
FormRepeater("MyRepeater")

Common Methods

For a list of common layout component methods, see here.

Schema

A repeater has a schema that is duplicated whenever the user request another entry, or in the case of loading a resource, based on how many elements are in an array.

go
// The Name and Grade of a student can be duplicated, 
// allowing you to specify an array of students to attach to your model.
FormRepeater("Students").
    Schema(
        FormInput("Name"),
        FormSelect("Grade"),
    )

Relationships

Repeaters make the most sense in the context of relationships or embedded arrays within your form's core model.

Naming your repeater

The name of the repeater must match the exact name of your associated relation or array. See below for additional examples.

Example

Here we have a model of subjects and an embedded model of students. In other words, each subject can have a list of students

go
type Subject struct {
    Name string
    StartDate time.Time
    EndDate time.Time
    Students []Students
}

type Student struct {
    Name string
    Age int
}

In order to edit and control a list of students apart of your Subject, you could create a repeater.

go
Form(driver).
    Schema(
        FormInput("Name"),
        FormDateTime("StartDate"),
        FormDateTime("EndDate"),
        // Please note the name is an exact match to our field called Students
        FormRepeater("Students"). 
            Schema(
                FormInput("Name"),
                FormInput("Age").Numeric(),   
            )
    )

This will allow you to create dynamically create students, as well as view that relationship and each of it's fields, within your parent model's form!

GORM

This works exactly the same way for GORM based models!

GORM Joins

Iridium's gorm driver will automatically Preload the repeater's relationship based on its name.

For example, if in the above examples, those models were GORM models, Iridium would call query.Preload("Students") for you!

Please see the gorm driver link if you need greater control over how to build your query and relationship.

Deleteable

Deletable allows you to delete repeater elements. It will display a trash can in each repeater element's toolbar.

go
// Static
FormRepeater("repeater").
    Deletable()

// Dynamic
FormRepeater("repeater").
    DeletableFn(func (ctx FieldContext) bool {
        return true
    })

Collapsable

Collapsable allows users to collapse their repeater elements. It will display a chevron in each repeater element's toolbar.

go
// Static
FormRepeater("repeater").
    Collapsable()

// Dynamic
FormRepeater("repeater").
    CollapsableFn(func (ctx FieldContext) bool {
        return true
    })

Reactivity

Form elements inside a repeater can interact with any other form element using our reactivity hooks.

Pathing Via Index

Repeaters have a hidden index grid that need to be apart of your pathing when using our callbacks. Please see our examples below.

Example

Here is a simple form setup involving a form repeater:

go
FormInput("City"),
FormRepeater("Students").
    Schema(
        FormInput("Name"),
        FormInput("Grade")
    )

Pathing inward

You can have the City input interact with a repeater element as so:

go
FormInput("City").
    Live().
    AfterStateUpdated(func(state []string, ctx FieldContext) ([]string, error) {
        // Please note the index apart of the pathing!
        
        // Update the name of the first repeater
        ctx.Set("./Students/0/Name", "Joe Smith")
        // Update the name of the second repeater 
        ctx.Set("./Students/1/Name", "Larry Smith")
        return state, nil
    })
FormRepeater("Students").
    Schema(
        FormInput("Name"),
        FormInput("Grade")
    )

Pathing between elements

go
FormInput("City"),
FormRepeater("Students").
    Schema(
        FormInput("Name").
            Live().
            AfterStateUpdated(func(state []string, ctx FieldContext) ([]string, error) {
                // Updating the Name of any repeater element will
                // cause the `Grade` of the 2nd repeater element to be set
                // to "12". (Remember repeaters are 0 based)
                ctx.Set("../1/Grade", "12")
                
                // Additionally, it will set the Grade within this repeater element
                // to be set to "4"
                ctx.Set("Grade", "4")
                return state, nil
            }),
        FormInput("Grade")
    )

Pathing outwards

go
FormInput("City"),
FormRepeater("Students").
    Schema(
        FormInput("Name").
            Live().
            AfterStateUpdated(func(state []string, ctx FieldContext) ([]string, error) {
                // Please note the ../../
                // One '..' to escape this repeater card, and another '..'
                // to escape the entire repeater
                ctx.Set("../../City", "Toronto")
                return state, nil
            }),
        FormInput("Grade")
    )

Released under the MIT License.