What are Repeat Groups?
When building a form, there may be questions that should be displayed more than once to a user. For example, in a registration form, the number of children in a household may be collected along with the name and age of each child. A set of questions that are grouped together and set to repeat is referred to as a Repeat Group. 📖Learn more about Using Repeat Groups in Forms.
Repeat groups can have multiple questions with the same path (ex. /data/child_repeat/name). For this reason, special functions are required when writing logic within a repeat group.
The Parent Function: (..)
Using the parent function (..) allows access the parent of the current question. For example, if writing logic for the question "What is Your Name?", typing (..) will access the /data/child_repeat in logic which can then be used to access another question within the same repeat.
Use Case: Writing logic between questions in the repeat.
Example: ../child_dob_known = 'yes' will prompt the user to enter a child's birth date only if it's known.
Use Case: Display the value of another question in the label.
Example: <output value="../name" can display the data within the <outputvalue="/data/child_repeat/name" /> string
Note: If there are groups within a repeat, (..) may need to be repeated multiple times (I.e., ../../age).
The Position Function: position( )
In a hidden value, the position function (position ()) placed in a calculate condition will return the position of a question in a repeat group (i.e., first, second). The position function is zero-indexed meaning that numbering starts with zero (0) and continues.
Example: <output value="position(..)" /> applied to a question such as "Please enter the name for the child" would return zero(0) if referencing the first "child" in a four (4) children position.
Note: The position function should not be used on a label ("Child") that is duplicated in a repeat group. Numbering can be modified by adding (+ integer) to the string respectively. (Example: position(..)+1 ensures numbering starts at one (1).
❗Limitation: Using the position function may cause inconsistent behavior with nested elements and conditional usage. This issue can be avoided by using the position function in the default value of a question instead of in the calculate condition.
The Current Function: current( )
The current function (current( )) will return a relative value to questions inside a repeat group which can be used for any predicate filter that makes use of square brackets [ ].
Use Case: Lookup table, location, or instance references.
Example: is_currently_enrolled = current()/../currently_enrolled
Inside of a repeat group titled "Registration", the question "Are you currently enrolled?" is included and the users' response to this question (currently_enrolled) will be used to filter a lookup table question labelled "Facility". The lookup table question is a sibling of the parent currently_enrolled question, meaning they are both inside of the "Registration" repeat directly, and not inside of another group. The lookup table will filter "Facilities" based on whether a field on the lookup table (is_currently_enrolled) is set to 'yes'.
The current() function starts a reference to the current "Facility" question, so the "/.." step moves the reference back to the active "Registration" repeat group, and the currently_enrolled step moves the reference to the sibling question.
Note: When saving case properties, you may find that error messages are displayed when repeat group questions are included. This is due to the fact that questions inside repeat groups cannot be saved to cases.
Model Iteration inside of a Question List
📖Review Using Repeat Groups for Model Iteration.
Repeat groups that are inside of a question lists require a fixed repeat count, so a model iteration ID query cannot be used. Therefore, a hidden value outside the question list to store the number of items to iterate is needed along with a hidden value inside the repeat group to store the current position. Also, a question to fetch each item from the models is required.
Example: The following lookup table titled "Bunnies" includes items to be iterated. These items are "Flopsy", "Mopsy", "Cottontail" and "Peter".
Below is a form that iterates these items in a question list.
Relevant fields and respective values needed are shown in the table below:
Logic Outside of a Repeat Group
Paths can point to multiple questions so special functions must be used to access specific repeated values.
Access a Question by Count:
Accesses a specific question (ex. Question 2 in a group) using square brackets.
Example: /data/child_repeat/name will access the name of the first repeated child.
Note: This function can only be applied to text-based references and does not apply to Easy References.
Find the Number of Times a Question is Repeated: (count(question_path)
Returns the number of times a question is repeated.
Example: count(/data/child_repeat) will return the number of times a repeat group was repeated.
Filtering by Question Answer
Accesses a specific question based on an answer provided in a repeat group by using a filter in square brackets.
Example: count(/data/child_repeat[gender = 'female']) counts the number of children who are female.
Note: Multiple square brackets can be used for filters.
Removing Repeat Groups (Mobile)
In applications with user-controlled repeat counts, users can inadvertently create repeat groups that are not needed. Deleting groups can cause errors so mobile applications prevent users from creating repeat groups. If repeat groups are essential, they can be created by placing questions inside of a repeat into a group, and using the group's display condition to eliminate the questions inside of the repeat. This allows the user to be prompted to cancel the creation of a repeat group.
Below is an example of the way this is displayed on CommCareHQ in the Form builder.
Below is a table with question properties. Note: Fields that are not displayed in the table (I.e., validation condition, repeat count) have been left blank.