Authoring forms
A form is written once, by the people who own it, in whichever format suits them:
- The designer: an outline, a live preview and settings in plain words. No syntax at all. See The designer.
- Excel: one row per question, in the layout used by XLSForm (known from ODK and KoboToolbox). Office staff can work in a tool they already know, and changes are easy to review.
- YAML: for developers, version control and code review.
All three compile to the same canonical JSON, which is what runtimes read. Nobody edits the JSON by hand. The designer opens and saves Excel and YAML, so people can move between tools freely.
Structure
A form has pages, and pages have questions. Groups collect related questions. A group
also becomes a level in the submitted data: applicant + name is applicant.name.
pages:
- name: about_you
label: { da: Om dig, en: About you }
fields:
- name: applicant
type: group
fields:
- { name: name, type: text, label: { da: Navn, en: Name }, required: yes }A form without pages can list its fields directly; it then has one page.
Question types
| Type | People answer with | Submitted as |
|---|---|---|
text |
a line of text | text |
integer, decimal |
a number, typed the local way (1.234,50 in Danish) |
a number |
date |
a date | YYYY-MM-DD |
boolean |
a checkbox | true, or nothing |
select_one, select_multiple |
radios or a dropdown, or checkboxes | the chosen value(s) |
search |
type, then choose a suggestion (e.g. an address). See Lookups. | the chosen value |
note |
nothing: information text | — |
calculate |
nothing: a hidden calculated value | the result |
Rules
Every rule is a formula, written like in Excel. See Formulas for the full language.
| Setting | Meaning | Example |
|---|---|---|
relevant |
Show the question only when this is TRUE | employment = "self" |
required |
Must be answered (yes, or a formula) |
yes, married = "yes" |
constraint |
The answer is valid only when this is TRUE; . is the answer |
AGE(.) >= 18 |
readonly |
Cannot be changed | yes |
calculate |
The value is computed | ROUND(@valuation.value * 0.8, 0) |
default |
A pre-filled answer | 20 |
Hidden questions are left out of calculations and of the submitted data, so an answer someone gave before changing their mind cannot slip through.
Messages are yours to write, per language: requiredMessage and constraintMessage. Without them,
people see a standard message such as "Feltet skal udfyldes".
Options
Options are shared lists, or come from a lookup:
choices:
employment:
- { value: employed, label: { da: Lønmodtager, en: Employed } }
- { value: self, label: { da: Selvstændig, en: Self-employed } }
fields:
- { name: employment, type: select_one, choices: employment, label: … }
- { name: property, type: select_one, choices: "@properties", choiceValue: id, choiceLabel: address, label: … }Up to six options show as radios, and more as a dropdown.
Text
Every text can be given per language and can include answers or lookup results:
label:
da: "Forventet månedlig ydelse: {{ loan.monthly_payment | number: 2 }} kr."
en: "Expected monthly payment: DKK {{ loan.monthly_payment | number: 2 }}"See Text templates.
The same form in Excel
| Sheet | Columns |
|---|---|
settings |
id, version, title::da, title::en, languages |
survey |
type, name, label::da, label::en, hint::…, required, required_message::…, relevant, readonly, constraint, constraint_message::…, calculation, default, choice_value, choice_label, verify |
choices |
list_name, value, label::da, label::en |
datasources |
id, param, value, when, query |
begin page / end page and begin group / end group rows give the structure. A select question
is written as select_one employment or select_one @properties. Formulas may start with =, as in
Excel, and Danish Excel's ; between arguments works too. Extra columns, such as your own comments,
are ignored.
Checking a form
npx quillflow compile form.xlsx -o form.jsonProblems are reported where the author will look for them:
form.xlsx, sheet "survey", row 14, column "relevant": Unknown field "incme". Did you mean "income"?
form.yaml:23: These rules depend on each other in a circle: whether "a" is shown → the value of "b" → …The designer shows the same messages next to the setting they are about.