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
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
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:
field labels { type = array of string value = map($this in dependants, $this.name) }
Section 8 has the details.
choice — one question, several answers
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
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
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:
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.