What is an XLSForm?
XLSForm is a form standard created to help simplify the authoring of forms in Excel. Authoring is done in a human-readable format using a familiar tool that almost everyone knows - Excel. XLSForms provide a practical standard for sharing and collaborating on authoring forms. They are simple to get started with but allow for the authoring of complex forms by someone familiar with the syntax described below. The XLSForm is then converted to an ODK XForm, a popular open form standard, that allows you to author a form with complex functionality, like skip logic, in a consistent way across a number of web and mobile data collection platforms.
Basic format
Each Excel workbook usually has two worksheets: survey and choices. A third optional worksheet, called settings, can add additional specifications to your form and is described below.
The survey worksheet
This worksheet gives your form its overall structure and contains most of the content of the form. It contains the full list of questions and information about how they should appear in the form. Each row usually represents one question; however, there are certain other features described below that you can add to the form to improve the user experience.
The choices worksheet
This worksheet is used to specify the answer choices for multiple-choice questions. Each row represents an answer choice. Answer choices with the same list name are considered part of a related set of choices and will appear together for a question. This also allows a set of choices to be reused for multiple questions (for example, yes/no questions).
Setting up your worksheets
Both of these worksheets have a set of mandatory columns that must be present for the form to work. Additionally, each worksheet has a set of optional columns that allow further control over the behavior of each entry in the form, but are not essential to have. Every entry must have values for each of the mandatory columns, but the optional columns may be left blank.
- The survey worksheet has 3 mandatory columns: type, name, and label.
- The type column specifies the type of entry you are expecting for the question.
- The name column specifies the unique variable name for that entry. No two entries can have the same name. Names have to start with a letter or an underscore. Names can only contain letters, digits, hyphens, underscores, and periods. Names are case-sensitive.
- The label column contains the actual text you see for the question in the form. Alternatively, label translation columns can be used.
type | name | label |
---|---|---|
today | today | |
select_one gender | gender | Respondent's gender? |
integer | age | Respondent's age? |
survey |
- The choices worksheet has 3 mandatory columns as well: list name, name, and label.
- The list name column lets you group together a set of related answer choices, i.e., answer choices that should appear together under a question.
- The name column specifies the unique variable name for that answer choice.
- The label column shows the answer choice exactly as you want it to appear on the form. Alternatively, label translation columns can be used.
list_name | name | label |
---|---|---|
gender | transgender | Transgender |
gender | female | Female |
gender | male | Male |
gender | other | Other |
choices |
The columns you add to your Excel workbook, whether they are mandatory or optional, may appear in any order. Optional columns may be left out completely. Rows or columns may be left blank to aid readability, but data after 20 adjacent blank columns or rows on a sheet will not be processed. All .xlsx file formatting is ignored, so you can use dividing lines, shading, and other font formatting to make the form more readable.
One thing to keep in mind when authoring forms in Excel is that the syntax you use must be precise. For example, if you write Choices or choice instead of choices, the form won't work.
Question types
XLSForm supports a number of question types. These are just some of the options you can enter in the type column in the survey worksheet in your XLSForm:
Question type | Answer input |
---|---|
integer | Integer (i.e., whole number) input. |
decimal | Decimal input. |
range | Range input (including rating) |
text | Free text response. |
select_one [options] | Multiple choice question; only one answer can be selected. |
select_multiple [options] | Multiple choice question; multiple answers can be selected. |
select_one_from_file [file] | Multiple choice from file; only one answer can be selected. |
select_multiple_from_file [file] | Multiple choice from file; multiple answers can be selected. |
rank [options] | Rank question; order a list. |
note | Display a note on the screen, takes no input. Shorthand for type=text with readonly=true. |
geopoint | Collect a single GPS coordinate. |
geotrace | Record a line of two or more GPS coordinates. |
geoshape | Record a polygon of multiple GPS coordinates; the last point is the same as the first point. |
date | Date input. |
time | Time input. |
dateTime | Accepts a date and a time input. |
image | Take a picture or upload an image file. |
audio | Take an audio recording or upload an audio file. |
background-audio | Audio is recorded in the background while filling the form. |
video | Take a video recording or upload a video file. |
file | Generic file input (txt, pdf, xls, xlsx, doc, docx, rtf, zip) |
barcode | Scan a barcode, requires the barcode scanner app to be installed. |
calculate | Perform a calculation; see the Calculation section below. |
acknowledge | Acknowledge prompt that sets value to "OK" if selected. |
hidden | A field with no associated UI element which can be used to store a constant |
xml-external | Adds a reference to an external XML data file |
You can find a full list of question types and the appearances that can modify them in the template and reference.
Capturing GPS location
A question of type geopoint
captures the current geolocation from a device. To collect the name and GPS coordinates of a store, you would write the following in your form:
type | name | label |
---|---|---|
text | store_name | What is the name of this store? |
geopoint | store_gps | Collect the GPS coordinates of this store. |
survey |
To collect a line or shape of GPS coordinates, you can use one of the following:
type | name | label | hint |
---|---|---|---|
geotrace | pipe | Pipeline | Please walk along the pipeline and record the coordinates of each corner point |
geoshape | border | Border | Please walk along the border and record the coordinates of each corner point |
survey |
You can learn more on the geopoint widget in the ODK documentation.
Specifying a target accuracy at which to capture location
For geopoint
questions, ODK Collect automatically collects the GPS when an accuracy level of 5 meters or better is reached. You can change this default behaviour by specifying a value for the capture-accuracy parameter
. You can also include a warning-accuracy parameter (defaults to 100 meters). When the accuracy is that value or worse, the dialog will turn red and add a warning message stating that the accuracy is unacceptable.
For example, to automatically capture points with an accuracy of 10m or better while warning when accuracy is worse than 10m:
type | name | label | parameters |
---|---|---|---|
geopoint | store_gps | Collect the GPS coordinates of this store. | capture-accuracy=10 warning-accuracy=10 |
survey |
Multiple choice
XLSForm supports both select_one (select only one answer) and select_multiple (select multiple answers) questions. You can define choices for multiple choice questions directly in the form or, for very long choice lists or ones that need to be updated by an external process, in attached files.
Defining choices in your form requires adding a choices worksheet to your Excel workbook. Here is an example of a select_one question:
type | name | label |
---|---|---|
select_one yes_no | likes_pizza | Do you like pizza? |
survey |
list name | name | label |
---|---|---|
yes_no | yes | Yes |
yes_no | no | No |
choices |
Note that the yes_no in the survey worksheet must match the yes_no in the list name column in the choices worksheet. This ensures that the form displays the correct list of answer choices for a particular question.
We can also add multiple choice questions that allow multiple answers to be selected, like so:
type | name | label |
---|---|---|
select_multiple pizza_toppings | favorite_toppings | What are your favorite pizza toppings? |
survey |
list name | name | label |
---|---|---|
pizza_toppings | cheese | Cheese |
pizza_toppings | pepperoni | Pepperoni |
pizza_toppings | sausage | Sausage |
choices |
Choice names
The name
column of the choices sheet defines the values that will be saved when each choice is selected during data collection. Choice names for select_multiple must not contain spaces because spaces are used as a separator when an answer with multiple selected choices is saved. Choice names for select_one questions may contain spaces. However, we recommend avoiding them to make analysis easier. Additionally, this makes it possible to convert the question to a select_multiple in a future form version.
In general, choice names should be unique within a single-choice list. If two choices from the same list have the same name, they will be impossible to tell apart in analysis. If you have duplicate choice names, you will get an error, and your form will not be converted. However, it may sometimes be appropriate to have duplicate choice names. An example would be if you use a cascading select, and the choices with the same name are differentiated by a preceding question. If you do need to use duplicate choice names, you can suppress the error by using the allow_choice_duplicates
setting:
allow_choice_duplicates |
---|
yes |
settings |
Cascading selects
If you want question responses to filter the options available in later questions, use a cascading select. For example, if you want to ask which city the respondent lives in, instead of choosing from all cities in the world, you could first ask for their country. Then you could filter the list of states to only show the ones in the selected country, filter districts to only show the ones in the selected state, then filter cities to only show the ones in that district.
To chain or cascade selects, you will need to create a choice_filter column in your survey worksheet. The expression in this column will be used to filter down the list of choices for the corresponding select. Any choice for which the expression is true will be included. Check out an example XLSForm here.
Look up values in a choice list
You can add additional columns to the choices sheet and then look up values for those columns using the instance
function.
Specify other
For multiple-choice questions, surveys often include an option of marking other when their answer choice is not listed. Then, they are usually asked to specify the other option. XLSForm has a shortcut for doing this by adding or_other after the answer choice list name in the survey worksheet. The choices worksheet stays the same. See below:
type | name | label |
---|---|---|
select_multiple pizza_toppings or_other | favorite_topping | What are your favorite pizza toppings? |
survey |
list name | name | label |
---|---|---|
pizza_toppings | cheese | Cheese |
pizza_toppings | pepperoni | Pepperoni |
pizza_toppings | sausage | Sausage |
choices |
Click on the link to look at the complete pizza_questionnaire.
Select from map
To show select choices on a map, add the map or quick map appearance attribute to a select_one question. The choices sheet will also need a geometry column added for the list_name noted in the select_one questions. The geometry must be specified using the ODK format. This feature is only currently available on ODK Collect. See below:
list name | name | label | geometry |
---|---|---|---|
site | shofco | Shofco | 36.7965483 -1.3182517 0 0 |
site | gemkam | Gemkam Medical Clinic | 36.7967088 -1.3170903 0 0 |
site | silanga | Silanga Pharmacy | 36.7955008 -1.3167834 0 0 |
site | undugu | Undugu Medical Clinic | 36.7990986 -1.3179328 0 0 |
choices |
Multiple choice from file
If you want to provide the options for a multiple-choice question in a separate file, use the select_one_from_file or select_mutliple_from_file question type. Using separate files can make it easier to manage longer option lists, and to re-use option lists between surveys. Three types of files are supported: CSV, XML, and GeoJSON files. See usage examples below:
type | name | label | choice_filter |
---|---|---|---|
select_multiple_from_file country.csv | liv | In which countries did you live? | |
select_one_from_file countries.xml | cou | In which country do you live now? | |
select_one_from_file cities.xml | cit | What is the closest city? | country=${cou} |
select_one_from_file households.csv | hh | Select household number | |
survey |
type | name | label | appearance |
---|---|---|---|
select_one_from_file health_facility.geojson | site | Select the health facility visited | map |
survey |
The files require a specific format. A CSV file requires columns which represent the value and label of the options. If you use the column names name
and label
, these will be used automatically. You can also specify the columns to use.
An XML file requires a structure as shown below:
<root>
<item>
<name/>
<label/>
...
</item>
</root>
A GeoJSON requires each feature to have an id and a title property. The GeoJSON must be defined by a single top-level FeatureCollection. Learn more from the ODK documentation.
CSV, XML, and GeoJSON files may have additional columns, XML nodes, or features and custom properties as long as the above-mentioned basic requirements are met.
This question type is generally the preferred way of building select questions from external data as it is the most versatile and works across applications. However, selects from files with tens of thousands of options can affect the responsiveness of the form. If you have long choice lists, check whether your form is adequately responsive on the lowest performance device that your data collection team will use. If it is too slow, consider using Dynamic selects from preloaded data if your data collection application supports it.
Specify custom columns for label and value
If the CSV, XML, or GeoJSON files use different names for the choice name
and label
, add a column to the survey sheet named parameters
, and specify the custom names with the value
and label
parameters. See usage examples below:
type | name | label | parameters |
---|---|---|---|
select_multiple_from_file country.csv | liv | In which countries did you live? | value=ccode |
select_one_from_file countries.xml | cou | In which country do you live now? | label=cname |
select_one_from_file households.csv | hh | Select household number | value=housenum label=housename |
survey |
type | name | label | appearance | parameters |
---|---|---|---|---|
select_one_from_file health_facility.geojson | site | Select the health facility visited | map | value=id label=name |
survey |
Rank
The rank widget can be used to let respondents order a list of options. The answer is saved as an ordered, space-separated list of option values where all options are always included. The syntax is very similar to multiple-choice questions.
type | name | label |
---|---|---|
rank pizza_toppings | toppings | Order pizza toppings with your favorite on top |
survey |
list name | name | label |
---|---|---|
pizza_toppings | cheese | Cheese |
pizza_toppings | pepperoni | Pepperoni |
pizza_toppings | sausage | Sausage |
choices |
To prevent bias it is often recommended to use the randomize feature in conjunction with this widget.
Range
To restrict integer or decimal inputs to a specific range, you can use the range question. This question can be used with 3 optional space-separated parameters: start, end, and step in a parameters column. The default values are 0, 10, and 1 respectively. The example below will create a question that allows input from 0 until 17 with a step of 1. Using a decimal step will result in decimal values being collected.
type | name | label | parameters |
---|---|---|---|
range | amount | What is the age of the child? | start=0 end=17 step=1 |
survey |
To display a range question as a rating widget using stars, you can add the rating appearance as shown below:
type | name | label | appearance | parameters |
---|---|---|---|---|
range | rated | What rating do you give? | rating | start=1 end=5 step=1 |
survey |
Image
To upload an image file the image question type can be used. To ensure the images are not too large, you can optionally set the max-pixels parameter which will automatically downsize the uploaded image to match the largest side of the image with the pixel value provided.
type | name | label | parameters |
---|---|---|---|
image | img | Upload an image | max-pixels=1000 |
survey |
Audio recording quality
Certain clients use a value for quality in the parameters column to configure audio recording quality for question types audio or background-audio. Both question types accept quality values voice-only
, low
, and normal
. audio additionally accepts a quality of external
to specify that an external application should be used for recording.
type | name | parameters |
---|---|---|
audio | animal_sound | quality=normal |
survey |
Metadata
XLSForm has a number of data type options available for metadata collection:
Metadata type | Meaning |
---|---|
start | Start date and time of the survey. |
end | End date and time of the survey. |
today | Day of the survey. |
deviceid | Unique client identifier. Can be user-reset. |
phonenumber | Phone number (if available). |
username | Username configured (if available). |
Email address configured (if available). | |
audit | Log enumerator behavior during data entry |
Note that some metadata fields only apply to mobile phone-based forms.
For example, if you wanted to collect all of these types of metadata, put the following in your form (typically at the beginning, but can be at any point of your form):
type | name | label | parameters |
---|---|---|---|
start | start | ||
end | end | ||
today | today | ||
deviceid | deviceid | ||
phonenumber | phonenumber | ||
username | username | ||
audit | audit | [optional, see below] | |
survey |
Notice that there are no labels associated with the metadata question types. This is because the phone captures these variables automatically. These questions will not appear on the screen of the phone, but you will see them when viewing your submitted survey data. The Tutorial XLSForm shows how metadata is used in a form.
Audit enumerator behavior and location tracking
Note: For now this feature is only available in Collect, but not in Enketo webforms.
The audit metaquestion will enable ODK Collect to log how people navigate through a form during data entry. For example, this can be used to measure how much time an enumerator took to fill in a question, responses that were edited later on, or when the form was saved.
Optionally, the audit metaquestion can be configured to also record the location of the enumerator throughout the interview. This may be useful for quality control or to record the exact paths taken between each respondent. To do this, add a column called parameters to your form and enter three required parameters: location-priority, location-min-interval, and location-max-age.
This example below would collect the precise GPS location every 180 seconds and will discard coordinates collected more than 300 seconds ago.
type | name | label | parameters |
---|---|---|---|
audit | audit | location-priority=high-accuracy location-min-interval=180 location-max-age=300 | |
survey |
See this page in the ODK Collect documentation for full details about the audit metaquestion, available location tracking parameters, and the format of the audit.csv log file created for each submission.
Hints
Regular hints
Sometimes you want to add a small hint to a question on your form, instructing the user how to answer the question, but you don't want the hint to be part of the question itself. It’s easy to add hints to questions in XLSForms. Simply add a hint column and add your hint message. See below for an example.
type | name | label | hint |
---|---|---|---|
text | name | What is the name of this store? | Look on the signboard if the store has a signboard. |
geopoint | geopoint | Collect the GPS coordinates of this store. | |
survey |
The Tutorial XLSForm provides more examples of questions with hints.
Guidance hints
There is a special kind of hint that is normally not shown in the form. It is only shown in special views. An example would to show these hints on print-outs or during a training for enumerators. These hints are called guidance hints and can be added in the guidance_hint column. See below for an example.
type | name | label | guidance_hint | relevant |
---|---|---|---|---|
integer | age | Age? | ||
text | name | Name? | This will only be shown for age > 18. | ${age} > 18 |
survey |
Formulas
Formulas are used in the constraint, relevant, calculation, and trigger columns and optionally also in the default, and required columns. Formulas allow you to add additional functionality and data quality measures to your forms.
Formulas are composed of functions and operators (+,*,div,etc.). A well-documented full list of operators and functions can be found in the ODK documentation. For the technically inclined, the underlying XForms specification is the actual source document for the supported functions.
Look up values in choice lists or attached files
You can look up values from lists defined in the choices sheet, attached CSVs, attached geoJSON files and attached XML files by using the instance
function. You will use this general structure:
instance('list_name')/root/item[filter expression]/desired_property
The instance function needs the name of the list that you want to look up a value in. For lists specified in the choices sheet, this is the list_name
. For attached files, use the filename without the extension. For example:
- To look values up in a
fruits
list defined on the choices sheet, useinstance('fruits')
- To look values up in a
participants.csv
file, useinstance('participants')
- To look values up in a
places.geojson
file, useinstance('places')
The next part of the expression is /root/item[filter expression]
. /root/item
says to look at every item in the list and the provided filter expression will be used to include items for which the expression is true
and exclude items for which the expression is false
. This is the same kind of expression used in the choice_filter column. The most common kind of filter expression looks for an exact match on the name
of an item:
- To get the item in
participants.csv
withname
that matches a scanned barcode:instance('participants')/root/item[name=${barcode_id}]
- To get the item in
participants.csv
withname
that matches a value picked from a select:instance('participants')/root/item[name=${participant}]
The last part of the expression is a property or column name to access for the item(s) that match the filter expression:
- To get the age of a participant whose id card was scanned:
instance('participants')/root/item[name=${barcode_id}]/age
- To get the first name of a participant selected from a list:
instance('participants')/root/item[name=${participant}]/fname
This is generally the preferred way of looking values up in attached files as it is the most versatile and works across applications. However, looking up values in files with many tens of thousands of options can affect the responsiveness of the form. If you have long choice lists, check whether your form is adequately responsive on the lowest performance device that your data collection team will use. If it is too slow, consider using pulldata()
if your data collection application supports it.
💡 These expressions use a subset of XPath 1.0. The filter expression in square brackets can be any expression that evaluates to true or false, including using functions.
External CSV data
If you want to attach a CSV file to your form so that you can look values up in it, you have a few options. If you need to build a select from values in that CSV, you can use select_one_from_file. This will attach the CSV to your form and also allow you to look values up in it as described above.
If you don't need to build a select from values in your CSV, you can use the type csv-external and specify the name of the file without extension:
type | name | label | calculation |
---|---|---|---|
csv-external | participants | ||
barcode | id | Scan id | |
calculate | first_name | instance('participants')/root/item[participant_id=${id}]/fname | |
survey |
The sample form above attaches the participants.csv
list to the form. It then asks for a barcode scan and uses the scanned value to look up the participant with matching id in the participant_id
column. The value in that participant's fname
column is stored in the first_name
calculate.
External XML data
For users who need to perform complex queries on external data without restrictions, an external XML data file can be added with question type xml-external. The value in the name column can be used to refer to this data in any formula (e.g. for a calculation, constraint, relevant, or choice_filter) using the instance('name') function. A file with the same name and the .xml extension should be uploaded with the form. See below for an example that requires uploading a file called houses.xml with the form.
type | name | label | calculation |
---|---|---|---|
xml-external | houses | ||
integer | rooms | How many rooms? | |
calculate | count | count(instance('houses')/house[rooms = current()/../rooms ]) | |
survey |
Note that XML files can have any structure so instance
calls to look up values in an XML file may not be followed /root/item
.
Constraints
One way to ensure data quality is to add constraints to the data fields in your form. For example, when asking for a person's age, you want to avoid impossible answers, like -22 or 200. Adding data constraints in your form is easy to do. You simply add a new column, called constraint, and type in the formula specifying the limits on the answer. In the example below, the answer for the person's age must be less than or equal to 150. Note how the .
in the formula refers back to the question variable.
type | name | label | constraint |
---|---|---|---|
integer | age | How old are you? | . <= 150 |
survey |
In this example, the formula . <= 150
is saying that the value entered .
for the question must be less than or equal to 150. If the user puts 151 or above as the answer, s/he will not be allowed to move on to the next question or submit the form.
Other useful expressions to use in the constraint column can be found here. Look under the Operators section.
Constraint message
If you want to include a message with your constraint, telling the user why the answer is not accepted, you can add a constraint_message column to your form. See the example below.
type | name | label | constraint | constraint_message |
---|---|---|---|---|
integer | respondent_age | Respondent's age | . >=18 | Respondent must be 18 or older to complete the survey. |
survey |
In this example, if the user enters an age less than 18, then the error message in the constraint_message column appears. More examples on constraints have been illustrated in this XLSForm.
Relevant
One great feature of XLSForm is the ability to skip a question or make an additional question appear based on the response to a previous question. Below is an example of how to do this by adding a relevant column for a select_one question, using our pizza topping example from before:
type | name | label | relevant |
---|---|---|---|
select_one yes_no | likes_pizza | Do you like pizza? | |
select_multiple pizza_toppings or_other | favorite_topping | Favorite toppings | ${likes_pizza} = 'yes' |
survey |
In this example, the respondent is asked, “Do you like pizza?” If the answer is yes, then the pizza topping question appears below. Note the ${ }
around the variable likes_pizza. These are required in order for the form to reference the variable from the previous question.
Setting relevance based on the value of a select_multiple question is slightly different from the select_one question example above:
type | name | label | relevant |
---|---|---|---|
select_one yes_no | likes_pizza | Do you like pizza? | |
select_multiple pizza_toppings or_other | favorite_topping | Favorite toppings | ${likes_pizza} = 'yes' |
text | favorite_cheese | What is your favorite type of cheese? | selected(${favorite_topping}, 'cheese') |
survey |
list name | name | label |
---|---|---|
pizza_toppings | cheese | Cheese |
pizza_toppings | pepperoni | Pepperoni |
pizza_toppings | sausage | Sausage |
choices |
Since the pizza topping question allows multiple responses, we have to use the selected(${favorite_topping}, 'cheese')
expression, because we want the cheese question to appear every time the user selects cheese as one of the answers (regardless of whether additional answers are selected).
In the Specify Other section it is mentioned that relevance could be used to specify a free-text "other" option for multiple-choice questions. The advantages of doing this over using the or_other shortcut are that you get control over the choice name, you get to specify the text that the user sees, and it works with multi-language forms. Here is an example:
type | name | label | relevant |
---|---|---|---|
select_multiple pizza_toppings | favorite_toppings | What are your favorite pizza toppings? | |
text | favorite_toppings_other | What other toppings do you like? | selected(${favorite_toppings}, 'other') |
survey |
list name | name | label |
---|---|---|
pizza_toppings | cheese | Cheese |
pizza_toppings | pepperoni | Pepperoni |
pizza_toppings | sausage | Sausage |
pizza_toppings | other | Other |
choices |
Note that you must include other as an answer choice in the choices worksheet.
Calculation
Your survey can perform calculations using the values of the preceding questions. In most cases using a calculate type question is appropriate. For example, in the survey below, we have calculated the tip for a meal and displayed it to the user:
type | name | label | calculation |
---|---|---|---|
decimal | amount | What was the price of the meal? | |
calculate | tip | ${amount} * 0.18 | |
note | display | 18% tip for your meal is: ${tip} | |
survey |
Note that the ${tip} in the last line will be replaced with the actual tip amount when viewing and filling out the form.
The calculate type calculates text but calculations can also be added to any other question types. Non-text types can be useful for data analysis, e.g. if a date or date-time is calculated. If no label and no hint are included, the calculation will be hidden. See the example below which is the equivalent of the previous form:
type | name | label | hint | calculation |
---|---|---|---|---|
decimal | amount | What was the price of the meal? | ||
text | tip | ${amount} * 0.18 | ||
note | display | 18% tip for your meal is: ${tip} | ||
survey |
And this is an example of when a non-text type is needed because of data analysis requirements:
type | name | label | hint | calculation |
---|---|---|---|---|
date | day | today() | ||
survey |
Note that using non-text calculation types has no effect on using the calculation result within the form itself. This is a common misunderstanding.
If a label or hint is included, the question will be visible on the form and the calculated value will be shown in the input field or widget. This is generally only recommended for readonly questions to avoid re-calculating (erasing) a user-entered value. See the example below:
type | name | label | readonly | calculation |
---|---|---|---|---|
decimal | amount | What was the price of the meal? | ||
note | display | 18% tip for your meal is: | ${amount} * 0.18 | |
date | today | Today's date is: | true | today() |
survey |
Note the difference with the first form in this section is how the calculated tip value is displayed. In the first example it was shown in the label and in the last example it is shown inside a readonly input field.
Trigger
A trigger column can be used to run a calculation only when another visible question in the form changes. This means that the question that is serving as the trigger has to have a label or a hint (otherwise it will be hidden). See a simple but very useful example below:
type | name | label | calculation | trigger |
---|---|---|---|---|
integer | temp | Enter the current temperature | ||
dateTime | temp_ts | now() | ${temp} | |
survey |
This will calculate a timestamp immediately after a respondent enters a temperature. If the user goes back and changes the temperate, the timestamp will be re-calculated.
All the regular calculation features apply to these special value-change-triggered calculations as well. So you can e.g. use a label or hint to display the calculation question on the form to the user.
Multiple questions may have the same trigger. See this example, where two calculations are triggered by the temperature question (one is hidden, and the other is shown):
type | name | label | calculation | trigger | readonly |
---|---|---|---|---|---|
integer | temp | Enter temperature in Celsius | |||
dateTime | temp_ts | now() | ${temp} | ||
text | temp_F | Temperature in Fahrenheit | 32 + 1.8 * ${temp} | ${temp} | true |
calculate | temp_K | 273.15 + ${temp} | ${temp} | ||
survey |
In the form above the temp_F question is shown to the user and the temp_K question is hidden, just as they would be if the trigger was not used.
An important and powerful difference with regular calculations is that the calculation value with a trigger may also be empty, which serves to clear a value from the form. See the example below:
type | name | label | calculation | trigger |
---|---|---|---|---|
text | name | What is the name of the oldest person here? | ||
integer | age | How old is this person? | ${name} | |
survey |
If the respondent using this form has entered the name and age of person A and subsequently finds out there is an older person B, the age field will be cleared as soon as the name of person B has been entered.
Required
It's simple to mark certain questions as required in your form. Marking them as required means the user will not be able to move on to the next question or submit the form without entering an answer for that question.
To make questions required, add a required column to your survey worksheet. Under that column, mark questions as required by writing yes. See the example below:
type | name | label | constraint | required |
---|---|---|---|---|
integer | age | How old are you? | . <= 150 | yes |
survey |
Required message
If you want to customize the message displayed to users when they leave a required question blank, you can add a required_message column to your form. See the example below.
type | name | label | required | required_message |
---|---|---|---|---|
integer | respondent_age | Respondent's age | yes | Sorry, this answer is required. |
survey |
Randomize Choices
For any question type that shows a list of choices the shown order of the choices displayed to the user can be randomized with the parameters column. See below:
type | parameters | name | label |
---|---|---|---|
select_one toppings | randomize=true | top | Favorite? |
survey |
For reproducible randomization, a seed can be explicitly provided as shown below. To learn more about the randomization algorithm used, see here.
type | parameters | name | label | calculation |
---|---|---|---|---|
calculate | sd | once(decimal-date-time(now())) | ||
select_one toppings | randomize=true, seed=${sd} | top | Favorite? | |
survey |
Note that once()
is used to prevent re-randomizing for example when a draft record is loaded for editing.
Grouping questions
To create a group of questions in your form, use the begin_group…end_group syntax.
type | name | label |
---|---|---|
begin group | respondent | Respondent |
text | name | Enter the respondent’s name |
text | position | Enter the respondent’s position within the school. |
end group | ||
survey |
This is a good way to group related questions for data export and analysis. Notice how end group doesn't require a name or label, because it is hidden in the form.
In your form, you can use the field-list appearance on a group to display multiple questions on the same screen.
Nesting groups within groups
Groups of questions can be nested within one another:
type | name | label |
---|---|---|
begin group | hospital | Hospital |
text | name | What is the name of this hospital? |
begin group | hiv_medication | HIV Medication |
select_one yes_no | have_hiv_medication | Does this hospital have HIV medication? |
end group | ||
end group | ||
survey |
You always have to end the most recent group that was created first. For instance, the first end group you see closes the HIV medication group, and the second one closes the beginning hospital group. When working with groups and you keep getting error messages when trying to upload your form, double-check that for each begin group you have one end group.
Skipping
One neat feature of XLSForm is the ability to skip a group of questions by combining the group feature with relevant syntax. If you want to skip a group of questions all at once, put the relevant attribute at the beginning of a group like follows:
type | name | label | relevant |
---|---|---|---|
integer | age | How old are you? | |
begin group | child | Child | ${age} <= 5 |
integer | muac | Record this child’s mid-upper arm circumference. | |
select_one yes_no | mrdt | Is the child’s rapid diagnostic test positive? | |
end group | |||
survey |
In the above example, the two child group questions (muac and mrdt) will only appear if the child's age from the first question is less than or equal to five.
Repeats
A user can repeat questions by using the begin repeat and end repeat construct:
type | name | label |
---|---|---|
begin repeat | child_repeat | |
text | name | Child's name |
decimal | birthweight | Child's birthweight |
select_one male_female | sex | Child's sex |
end repeat | ||
survey |
list name | name | label |
---|---|---|
male_female | male | Male |
male_female | female | Female |
choices |
In the above example, the name, birthweight, and sex fields are grouped together in a repeat, and the user can collect the same information about multiple children by selecting the option in the form to add another repeat.
The label column is optional for begin repeat. Assigning a label to a repeat will add the label as a title to the block of repeat questions in the form. A meaningful label will help enumerators and participants navigate the form as intended. We generally recommend using a singular noun or noun phrase such as observation or household member.
This interaction may be confusing to users the first time they see it. If enumerators know the number of repetitions ahead of time, consider using a dynamically defined repeat count.
When a repeat is shown in a table of contents, the label used to represent each repeat is the label of the first group inside that repeat. In the example below, if a repeat is filled out with values Preity for first_name
, Zinta for last_name
and 71 for age
, that repeat will be summarized as Preity Zinta - 71:
type | name | label |
---|---|---|
begin repeat | person_repeat | |
begin group | person | ${first_name} ${last_name} - ${age} |
text | first_name | First name |
text | last_name | Last name |
integer | age | Age |
end group | ||
end repeat | ||
survey |
The Delivery Outcome XLSForm is another repeat example.
Fixed repeat counts
Instead of allowing an infinite number of repeats, the form designer can specify an exact number of repeats by using the repeat_count column:
type | name | label | repeat_count |
---|---|---|---|
begin repeat | child_repeat | 3 | |
text | name | Child's name | |
decimal | birthweight | Child's birthweight | |
end repeat | |||
survey |
In the above example, exactly 3 child repeats will be created.
Dynamic repeat counts
The repeat count can be set to an expression that refers to other fields in the form. In the example below, the number that the user inputs for the num_hh_members field dictates the number of hh_member repeats added:
type | name | label | repeat_count |
---|---|---|---|
integer | num_hh_members | Number of household members? | |
begin repeat | hh_member | ${num_hh_members} | |
text | name | Name | |
integer | age | Age | |
end repeat | |||
survey |
Only add repeats in certain conditions
Like with groups, all of the questions in a repeat can be skipped based on some condition. In the example below, the person filling out the form will only be given the opportunity to add children if they first indicate that there are children to add:
type | name | label | relevant |
---|---|---|---|
select_one yes_no | has_child | Do any children live here? | |
begin repeat | child_repeat | ${has_child} = 'yes' | |
text | name | Child's name | |
decimal | birthweight | Child's birthweight | |
end repeat | |||
survey |
list_name | name | label |
---|---|---|
yes_no | yes | Yes |
yes_no | no | No |
choices |
Representing zero repeats
By default, the person filling the form will see the questions corresponding to one repeat before getting the option to add more. To represent 0 repeats, there are three options:
- train the people filling out the form to delete the first repeat added
- if the exact number of repeats is known ahead of time, use a dynamic repeat count
- if the exact number of repeats is not known ahead of time, use relevant to only prompt the user for repeats if there are some to add
Multiple language support
It’s easy to add multiple languages to a form. You simply have to name your label::language1 (code), label::language2 (code), etc., and your forms will be available in multiple languages. See the example below. Select a different form language from the pulldown menu of the data collection application (this may be located under the Menu key). For the form below, English and Español will show up as the possible options.
type | name | label::English (en) | label::Español (es) | constraint |
---|---|---|---|---|
integer | age | How old are you? | ¿Cuántos años tienes? | . <= 150 |
survey |
You can also add different language columns for hints and media files by using the same ::language (code)
construct, as shown in the example below. See also the XLSForm reference, which includes a list of all column headers that can accept a language modification.
hint::English (en) | hint::Dutch (nl) | image::English (en) | image::Dutch (nl) |
---|---|---|---|
a hint | een hint | old_person_cartoon.png | ouwe_strip.png |
survey |
Form language and user interface language may be determined separately by the application and may not match. To facilitate matching both (in the future), it is recommended, though optional, to add a 2-character language code after the language name. The official 2-character language codes, called subtags are published here (search the page with Ctrl-F or Cmd-F).
If your form defines multiple languages and you want to set one language for data collection, set an explicit default language. This is done on the XLSForm's settings sheet by specifying the language in the default_language column. In the example [le below, the default language will be French.
form_id | version | default_language |
---|---|---|
test_form | 101 | French (fr) |
settings |
Media
You can include questions in your form that display images or that play video or audio files. If using the ODK mobile client for form submission, you need to put the media files that you want to include in the /odk/forms/formname-media folder on your phone, and then reference the exact file name in the media column in your form. See below for an example of how to do this.
type | name | label | image | video |
---|---|---|---|---|
note | media_example | Media example | example.jpg | example.mp4 |
survey |
Check out the Birds XLSForm which illustrates the use of media files. You can also click on the link to see the Birds webform .
Media is translatable in the same way as labels and hints as explained in the languages section.
Default
Adding a default field means that a question will be pre-populated with an answer when the user first sees the question. This can help save time if the answer is one that is commonly selected or it can serve to show the user what type of answer choice is expected. See the example below.
type | name | label | default |
---|---|---|---|
date | survey_date | Survey date? | 2010-06-15 |
decimal | weight | Respondent's weight? (in kgs) | 51.3 |
survey |
The respondent can simply change the answer by tapping in the answer field and entering another answer.
You can also add a default calculation, which will only be calculated only once when the form loads or - if the question is inside a repeat - when the repeat is added.
type | name | label | default |
---|---|---|---|
date | d | Enter the date the event occurred? | today() |
survey |
Read only
Adding a read only field means that a question can not be edited. Read-only fields can be combined with default fields to deliver information back to a user.
type | name | label | read_only | default |
---|---|---|---|---|
integer | num | Please patient is: | yes | 5 |
survey |
Appearance
The appearance column allows you to change the appearance of questions in your form. The following table lists the possible appearance attributes and how the question appears in the form.
Appearance attribute | Question type | Description |
---|---|---|
multiline | text | Best if used with web clients, makes the text box multiple lines long. |
minimal | select_one, select_multiple | Answer choices appear in a pull-down menu. |
quick | select_one | Relevant for mobile clients only, this attribute auto-advances the form to the next question after an answer is selected. |
no-calendar | date | For mobile devices only, used to suppress the calendar. |
month-year | date | Select a month and year only for the date. |
year | date | Select only a year for the date. |
horizontal-compact | select_one, select_multiple | For web clients only, this displays the answer choices horizontally. |
horizontal | select_one, select_multiple | For web clients only, this displays the answer choices horizontally, but in columns. |
likert | select_one | Best if used with web clients, makes the answer choices appear as a Likert scale. |
compact | select_one, select_multiple | Displays answer choices side by side with minimal padding and without radio buttons or checkboxes. Particularly useful with image choices. |
quickcompact | select_one | Same as previous, but auto-advances to the next question (in mobile clients only). |
field-list | groups | Entire group of questions appear on one screen (for mobile clients only). |
label | select_one, select_multiple | Displays answer choice labels (and not inputs). |
list-nolabel | select_one, select_multiple | Used in conjunction with the label attribute above, displays the answer inputs without the labels (make sure to put label and list-nolabel fields inside a group with field-list attribute if using mobile client). |
table-list | groups | An easier way to achieve the same appearance as above, apply this attribute to the entire group of questions (might slow down the form a bit). |
signature | image | Allows you to trace your signature into your form (mobile clients only). |
draw | image | Allows you to sketch a drawing with your finger on the mobile device screen. |
map, quick map | select_one, select_one_from_file | Allows a user to select a choice from many features on a map |
An XLSForm with all of the appearance attributes in this table is available here.
Settings worksheet
The settings worksheet is optional, but it is highly recommended to specify form_title, form_id and version at a minimum. Other settings allow you to further customize your form, including setting an overall style theme or encrypting your records.
An example settings worksheet is below:
form_title | form_id | version | instance_name | default_language | public_key |
---|---|---|---|---|---|
Example | ex_id | 2017021501 | concat(${firstname}, ' ', ${lastname}) | English (en) | IIBIjANBg… |
settings |
The settings column headings available are:
- form_title: The title of the form that is shown to users. The form title is pulled from form_id if form_title is blank or missing.
- form_id: The name used to uniquely identify the form on the server. The form ID is pulled from the XLS file name if form_id is blank or missing.
- version: String that represents this version. A common convention is to use strings of the form 'yyyymmddrr'. For example, 2017021501 is the 1st revision from Feb 15th, 2017.
- instance_name: Expression using form fields to identify each form submission. Learn more.
- default_language: In localized forms, this sets which language should be used as the default. The same format as described for adding translations should be used, including the language code.
- public_key: For encryption-enabled forms, this is where the public key is copied and pasted. Learn more.
- submission_url: This url can be used to override the default server where finalized records are submitted. Learn more.
- style: For web forms, specify the form style. Learn more.
- name: XForms root node name. This is rarely needed, learn more.
- clean_text_values: This column can be used with values
yes
orno
to control whether whitespace in the Survey sheet is collapsed (defaults toyes
).
Encrypted forms
Encryption-enabled forms provide a mechanism to keep finalized records private at all times. This includes the time after a record is marked as final that it is stored on the device and server as well as during transport, even when HTTP is used for communication. Encrypted records including their uploaded files, such as photos, are completely inaccessible to anyone not possessing the private key.
To encrypt XLS forms, add the public_key column to the settings worksheet and paste the base64-encoded public RSA key as its value.
form_id | public_key |
---|---|
mysurvey | IIBIjANBgklawWEserewrwesgdreewrwe32serfserfewrwerewtwer23sgfrqjwerk3423432… |
settings |
For more information on encrypted forms and how to generate the RSA keys have a look at the ODK documentation and at this example form.
Specify an alternative server
It is possible to specify an alternative server to send your submissions to in the submission_url column on the settings worksheet. Make sure to use the full URL that submissions should be sent to including the path.
If this column is left out or kept empty, submissions will go to the default destination for the provider you are using for your surveys.
Specify form submission name
In the settings worksheet, you can specify a unique name for each form submission using fields filled in by the user during the survey. On the settings worksheet, add a column called instance_name. Write in the expression that defines the unique form instance name using fields from the survey worksheet.
Check out this example XLSForm that calculates the instance name as the user's last and first names coupled with the form submission uuid.
Specify XForms root node name
In some rare cases, it may be helpful to explicitly specify a root node name for the generated XForm. For example, this may be necessary if updating a form that was converted with an older form converter that used a root node name other than data
. In the settings worksheet, you can specify an identifier to use for the XForms root node name by adding a column called name. By default, the XForms root node name is data
.
Multiple webpage forms
Web forms can be split into multiple pages using the style theme pages.
An example of a form divided into multiple pages can be seen on the Widgets on Pages webform.
In the settings tab, create a column called style and set it to pages, as follows:
form_title | form_id | style |
---|---|---|
example title | example_id | pages |
settings |
In your survey tab, group together the questions you would like to appear on each page and then set the appearance for the group to field-list. See the example below.
type | name | label | appearance |
---|---|---|---|
type | name | label | appearance |
begin group | group1 | field-list | |
text | name | Respondent's name | |
integer | age | Respondent's age | |
text | address | Respondent's address | |
end group | |||
survey |
See this blog post for more information on creating multi-page web forms. The XLSForm source is here.
Grid theme forms
The theme-grid style allows your form to mimic the look of traditional paper surveys by compacting multiple questions into one row. This style is best used with larger screens (e.g., computers or tablets). It also makes a nice printout!
Please click on the link to see an example of a Grid theme webform.
To create a Grid form, in the settings tab, under the style column, write theme-grid, as follows:
form_title | form_id | style |
---|---|---|
example title | example_id | theme-grid |
settings |
In your survey tab, group together the questions you would like to appear in each section and then set the appearance for each field according to the desired width (the default width is 4). See the example below.
type | name | label | appearance |
---|---|---|---|
begin group | group1 | ||
text | name | Respondent's name | w3 |
integer | age | Respondent's age | w1 |
text | address | Respondent's address | w4 |
end group | |||
survey |
See this blog post for more information on creating Grid forms. The Grid theme XLSForm example is here.
Styling prompts
Markdown support in XLSForm allows for increased emphasis through bold and italics, different-sized headers, various fonts and colors, and clickable web links in ODK Collect 1.4.9 and Enketo.
- emphasize words by wrapping them inside
_
or*
- strongly emphasize words by wrapping them inside
__
or**
- add a link by using
[name of link](url)
- add various sized headers by prepending # (biggest) to ###### (smallest) to header text
- style text for color or font with span tags (e.g.,
<span style="color:#f58a1f">
orange</span>
,<span style="color:red; font-family:cursive">
red and cursive</span>
) - add a line break where you want it with Ctrl-Enter or Ctrl-Alt-Enter (may be a different key combination for some spreadsheet software)
- add your favorite emojis 😍📋😍!
- use superscript with the
<sup>
tag (e.g.100 m<sup>2</sup>
turns into 100 m2) - use subscript with the
<sub>
tag (e.g.H<sub>2</sub>O
turns into H2O) - use the
\
character before#
,*
,_
, and\
to prevent special styling effects from being triggered by these characters
Advanced use and extensibility
It is possible to use XLSForm to create XForms with custom or experimental features. This is great for custom applications with a specific feature that is not suitable for the larger community.
The survey sheet has support for 3 column prefixes (instance::, bind::, body::) that add attributes to the XForm output, either in the primary instance, bind, or form control. To learn more about XForms visit the ODK XForms Specification. The example below adds a custom "hxl" attribute to the primary instance node of a question.
type | name | label | instance::hxl |
---|---|---|---|
integer | population | How many people are present? | #population |
survey |
The settings sheet has support for defining (multiple space-separated) additional custom namespaces and namespace prefixes using the namespaces column. You'll then be able to use those namespaces in the survey sheet, for example, to properly define a custom attribute with your organisation's own namespace. Note that the column name prefix has 2 colons (e.g. bind::
), while the namespace prefix has 1 colon (e.g. esri:
). See example below that adds 2 additional namespaces and uses them to add custom attributes:
title | namespaces |
---|---|
My Form | esri="http://esri.com/xforms" enk="http://enketo.org/xforms |
settings |
type | name | label | bind::esri:fieldLength | bind::enk:for |
---|---|---|---|---|
text | desc | Describe | 50 | |
text | desc_comment | Comments | ${a} | |
survey |
The settings sheet has support for 1 column prefix (attribute::) that adds attributes to the XForm output in the main instance element (named data
by default). For example, the below would appear in the XForm output like: <data id="my_form" xyz="1234"/>
.
title | attribute::xyz |
---|---|
My Form | 1234 |
settings |
As with the above survey columns, the attribute:: setting can be combined with the namespaces
setting to add a namespaced attribute. For example, the below would appear in the XForm output like: <data id="my_form" abc:xyz="1234"/>
.
title | attribute::abc:xyz |
---|---|
My Form | 1234 |
settings |
Tools that support XLSForms
- Ona
- Enketo
- ODK
- KoBoToolBox
- CommCare
- SurveyCTO
- Secure Data Kit (SDK)
- Survey123 for ArcGIS
- Community Health Toolkit
- CyberTracker
Appendix - loading big CSVs
instance
function. They may be more performant in some tools that allow filling XLSForms but may not work in others.
Data preloading
Pre-loading data is done when one wants to reference pre-existing data in a survey form. You can reference data in your survey form (the survey you are now authoring), from pre-existing data in a specific survey form or from any other source. For example, if you have pre-existing data from a household survey and you want to collect follow-up data about the household occupants. You can reference the household survey data in your survey form.
To reference pre-existing data in a survey form:
- Upload one or more .csv files as support files when you upload your form definition (the same way you upload media support files as explained in the Media section). The first row of each .csv file should be a header that includes short:
- unique names for each column
- subsequent rows which should contain the data itself
Each csv file should contain at least one column that can be used to uniquely identify each row. Such columns will be used, at survey time, to look up which row's data to pull into the survey. For the columns that will be used for looking up rows add _key to the end of the column name in the first row. Any columns with names ending in _key will be indexed for faster look-ups on your survey devices. See below an example of the columns on a .csv file:
name_key | name |
---|---|
mango | Mango |
orange | Orange |
How to pull data from CSV
instance
function to look values up in that file rather than pulldata
.
You can be able to pull data from .csv file by including one or more .csv files in your form during the survey time. For each data field that you want to pull into your survey:
- Add a calculate field to your survey.
- Give that field a name
- Then in its calculation column, call the pulldata() function, indicating which field to pull from which row of which .csv file.
See below for an example:
type | name | label | calculation |
---|---|---|---|
calculate | fruit | pulldata('fruits', 'name', 'name_key', 'mango') | |
note | note_fruit | The fruit ${fruit} pulled from csv. | |
survey |
Once you have loaded .csv data into a survey field using the pulldata() function, you can reference that field in later relevance conditions, constraints, and labels, just as you would reference any other field that was filled in by the user.
Click on the link to see an example of a pre-loading sample form and the .csv file used with form can be found here
Important notes on usage of pre-loaded data
- Save .csv file in UTF-8 format if pre-loaded data contains non-English fonts or special characters this enables your Android device to render the text correctly.
- Data fields pulled from a .csv file are considered to be text strings therefore use the int() or number() functions to convert a pre-loaded field into numeric form.
- If the .csv file contains sensitive data that you may not want to upload to the server, upload a blank .csv file as part of your form, then replace it with the real .csv file by hand-copying the file onto each of your devices.
Dynamic selects from pre-loaded data
Once your form has one or more pre-loaded .csv files, you can dynamically pull the choice lists for select_one and select_multiple fields from those .csv files. Multiple-choice fields with dynamic choice lists follow the same general syntax as regular, static select_one and select_multiple fields as previously covered in the Multiple choice questions section.
The following should be done:
- specify select_one listname or select_multiple listname in the type column (where listname is the name of your choice list)
- specify any special appearance styles in the appearance column
- include one or more rows for your listname on the choices worksheet.
Below is an example of the survey worksheet:
type | name | label | appearance |
---|---|---|---|
select_one fruits | fruits | Select a fruit | search('fruits') |
survey |
There are three differences when the choice list should be pulled from one of your pre-loaded .csv files:
- In the appearance column:
- Include a search() expression that specifies which .csv rows to include in the choice list.
- If the field should use a non-default appearance style. The non-default appearance style goes into the column first, followed by a space, then the search() expression. [e.g., quick search()]
- On the choices worksheet:
- a row should indicate which .csv columns to use for the label and selected value. As follows:
- list_name column: specify the name of your choice list as you normally would.
- name column: include the name of the .csv column to use for uniquely identifying selected choices.
- label column: include the name of the .csv column to use for labeling the choices.
Note:
If you wish to include multiple columns in the labels, include a comma-separated list of all columns to include. The name column will be dynamically populated based on the column name you put there, and the label column will be dynamically populated based on the column name(s) you put there.
- In your choices worksheet row, you may also include a .csv column name in the image column. If you do, the image filename to use will be pulled from the specified .csv column.
Note:
If you refer to image files in this way, you must always upload those image files as media file attachments when you upload your form to the server.
See below an example of the choices worksheet:
list name | name | label |
---|---|---|
fruits | name_key | name |
choices |
Click on the link to see an example of a search-and-select sample form and the .csv file used with form can be found here.
For the search() expression, there are a series of options to indicate which .csv rows to include in the choice list:
-
search(csvName): The single-parameter search expression includes all distinct rows as choices (e.g., "search('hhplotdata')"). All rows in the specified .csv file will be considered as choices, but only distinct rows – those with unique selection values – will be listed for the user. In other words, duplicates will be automatically filtered from the list shown to users.
-
search(csvName, "contains", columnsToSearch, searchText): This search expression includes all distinct rows that contain the specified text in the specified column(s) (e.g., "search('hhplotdata', 'contains', 'respondentname', ${nametofind})"). The third parameter specifies either a single column name to search, or a comma-separated list of column names to search. Rows with matches in any specified column will be included.
-
search(csvName, "startswith", columnsToSearch, searchText): This search expression includes all distinct rows that start with the specified text in the specified column(s) (e.g., "search('hhplotdata', 'startswith', 'respondentname', ${nameprefix})"). The third parameter specifies either a single column name to search, or a comma-separated list of column names to search. Rows with matches in any specified column will be included.
-
search(csvName, "endswith", columnsToSearch, searchText): This search expression includes all distinct rows that end with the specified text in the specified column(s) (e.g., "search('hhplotdata', 'endswith', 'respondentname', ${namesuffix})"). The third parameter specifies either a single column name to search, or a comma-separated list of column names to search. Rows with matches in any specified column will be included.
-
search(csvName, "matches", columnsToSearch, searchText): This search expression includes all distinct rows that exactly contain the specified text in the specified column(s) (e.g., "search('hhplotdata', 'matches', 'respondentname', ${nametofind})"). The third parameter specifies either a single column name to search, or a comma-separated list of column names to search. Rows with exact matches in any specified column will be included.
-
search(csvName, searchType, columnsToSearch, searchText, columnToFilter, filterText): Finally, any of the four search types above can be further filtered to only include a subset of .csv data. Simply add two extra parameters to any of the search types above, with the first extra parameter being the column name to filter and the second extra parameter being the exact value to filter. For whichever search is specified in the first four parameters, only rows exactly containing the sixth parameter value in the column named by the fifth parameter will be included (e.g., "search('hhplotdata', 'contains', 'respondentname', ${nametofind}, 'villageid', ${villageid})" to list all matching names within a particular village).
Additional notes on usage:
- Choices will be ordered, by default, in the order that they appear in your .csv file. If you want to specify a different ordering, include a numeric column in your .csv file named sortby; choices will be ordered numerically, according to the sortby column (if present).
- You can include one or more static choice options, in addition to the dynamic ones loaded from your .csv file. Simply include static choices, as you normally would, on the choices worksheet. These can appear before and/or after the row that indicates the columns to use for your dynamic choices. The one restriction is that the values you specify for your static choices in the name column must be numeric.
Database-backed "fast external itemsets"
Enabling external selects is straightforward.
- Instead of select_one for the prompt type, use select_one_external.
- Instead of the choices sheet, put external choices in the external_choices sheet.
See select_one_external form for an example that uses normal and external choices.
When an XLSForm with external choices is converted to an XForm, two files will be produced, the XForm (e.g., form-filename.xml) with all the normal choices and an itemsets.csv with the external choices.
The itemsets.csv file can be uploaded to any ODK-compatible server (e.g., ODK Aggregate) as a media file. It will be downloaded to any ODK-compatible (e.g., ODK Collect) like any other media file and saved to the [form-filename]-media folder. Clients like ODK Collect load media files from the SD card and so your form with a large number of choices will now load very quickly.
More resources
If you want to dig in deeper to understand XForms and go beyond the XLSForms information on this site, here are some resources:
- XForms as supported by the ODK ecosystem
- ODK Form design guidelines
- Ona Form design overview
- KoBoToolbox form design help center
About this site
XLSForm.org is a community-supported project aiming to create a common reference point for the XLSForm standard.
If you want to contribute to or improve this documentation, please visit our project's GitHub repo.
History
XLSForm was originally developed by Andrew Marder and Alex Dorey of the Sustainable Engineering Lab at Columbia University. As XLSForms became adopted by the ODK Community, SEL worked with the ODK Team to develop the current specification. PyXForm, the library used to convert XLSForms to XForms, is an open-source project supported by members of ODK, Ona, SurveyCTO, and KoBoToolbox.