FS FormScript

Groups, repeats, choices, sections

A form is not a flat list of questions. There are five ways of putting questions together, and they are not variations of each other: each one adds something different to the output document, or nothing at all.

The block It adds
group an object in the output
repeat an array in the output
choice a closed set of answers
section nothing — it is only for the renderer
page nothing — it is only for the renderer

group — a named set of questions

forms
form applicant version = "1.0" {
  group address {
    field street { type = string }
    field city   { type = string }
  }

  field label { type = string value = address.street + ", " + address.city }
}

A group contributes an object to the output document, and its fields are addressed from outside it by path: address.street.

repeat — a list of the same questions

forms
form household version = "1.0" {
  repeat dependants {
    max = 5
    field name { type = string }
    field age  { type = integer }
  }
}

A repeat contributes an array to the output document. The person can add and remove rows.

A repeat must declare max. It is not a style choice: max is the bound the cost model uses, so the runtime can say how expensive a form is before anybody fills it in. A repeat without a max is an error.

Rows are read with a comprehension, and $this is the current row:

forms-fragment
field labels { type = array of string value = map($this in dependants, $this.name) }

Section 8 has the details.

choice — one question, several answers

forms
form applicant version = "1.0" {
  choice employment {
    label = "Employment status"
    type = enum("employed" = "Employed", "self" = "Self-employed", "other" = "Other")
  }
}

A choice is a field whose type is an enum, and it is rendered as a set of options rather than as a text box.

A choice declares an enum type and nothing else. Writing options or optionsFrom on a choice is an error: a closed option set is already written down in the enum, and there is no second way to say it.

If you need options that come from a server, that is a field with optionsFrom — a different fact, and the document says which it is.

section — a heading, and nothing else

forms
form applicant version = "1.0" {
  section identity {
    label = "Identity"

    field first_name { type = string }
    field last_name  { type = string }
  }
}

A section introduces no scope and no data level. The fields inside it are addressed as first_name, not identity.first_name — the section is only a grouping for the renderer.

page — a step, and nothing else

forms
form applicant version = "1.0" {
  page "applicant" {
    label = "Applicant"

    section identity {
      field first_name { type = string }
    }
  }

  page "review" {
    label = "Review"
  }
}

A page is a section that the renderer may show one at a time. It introduces no scope and no data level either.

Putting them together

A real form uses all of them:

forms
form loan version = "1.0" {
  section applicant {
    label = "Applicant"

    group personal {
      field first_name { type = string }
      field last_name  { type = string }
      field birth_date { type = date }
    }

    group address {
      field street { type = string }
      field city   { type = string }
      field postcode { type = string }
    }
  }

  section details {
    label = "Details"

    repeat dependants {
      max = 5
      field name { type = string }
      field age  { type = integer }
    }

    choice employment {
      label = "Employment status"
      type = enum("employed" = "Employed", "self" = "Self-employed")
    }
  }

  output {
    personal.first_name: string
    personal.last_name: string
    personal.birth_date: date
    address.street: string
    address.city: string
    address.postcode: string
    dependants: array of object { name: string, age: integer }
    employment: enum("employed", "self")
  }
}

Look at the output block: the sections are not in it. The groups are, as objects. The repeat is, as an array. The choice is, as its enum.

The one rule to remember

A group or a repeat adds a level to the output. A section or a page does not. Everything else follows from that.