In some forms you may want to repeat a set of questions. For example, when registering a mother you may want to ask the number of children then repeat a set of questions for those children.
Repeat groups can be either be configured to repeat a fixed number of times or a user chosen number of times.
Option 1 - Fixed Number of Times
Add a question or hidden value that determines the number of times repeated. Then drag that question into the Repeat Count setting for the repeat group question. Note: you cannot just type a number into this box. You must drag a hidden value or question into it.
Note: If the repeat group is inside of a question list, you need to configure it to repeat a fixed number of times.
Option 2 - User Controlled
By default, CommCare will prompt the mobile worker asking if they would like to go through the repeat again after they have completed all the questions in the repeat group.
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 the repeat group.
When you are referencing values inside of a repeat by default the references you make will refer to the "relative" questions inside of the group, not in the questions in the group before or after the current one.
When using lookup tables or other constructs that make use of "predicate filters" (IE: expressions with brackets like locations[@id = 'home']), the use of the current() function is necessary to reference the questions in the current group. This is because inside of the 's, a relative reference (like ./sibling) is relative to the element which is being filtered, not the question.
The current() function translates to essentially mean the same thing as the question itself, after which two dots can be used to make a relative reference to that question.
Inside of a repeat named registration you have answered a question currently_enrolled, and want to use that answer to filter a lookup table question named facility within the repeat. The lookup table question is a "sibling" of the 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'.
In the lookup table's filter, you would set the value to be
is_currently_enrolled = current()/../currently_enrolled
this is needed because within the filter, if we had tried to reference currently_enrolled, it would refer to a field in the lookup table with that name, and not to a question in the form.
the current() function starts a reference which refers 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 node.
(This section assumes you are familiar with using Repeat Groups for Model Iteration.)
As mentioned above "If the repeat group is inside of a question list, you need to configure it to repeat a fixed number of times."
This makes model iteration inside a question list a little more complicated to set up, because you cannot use a "Model Iteration ID Query". You need a hidden value outside the question list to store the number of models to iterate, a hidden value inside the repeat group to store the current position, and a question to fetch each item from the models. Let us step through that.
Imagine we had a lookup table whose items we wanted to iterate. Here is a lookup table called "bunnies", with items "Flopsy", "Mopsy", "Cottontail" and "Peter".
And here is a form that iterates them inside a question list. The relevant fields and respective values needed for this set up can be seen in the table below:
|Display Text||Question ID||Question Type||Calculate Condition||Default Value||Repeat Count|
|repeat_group||Repeat Group||repeat group||#form/repeat_count|
|position||position||hidden value||position(..) + 1|
|<output value="current()/../position" /> - <output value="current()/../bunny" />||bunny_label||label|
The first question in the form is a hidden value with the self-descriptive name repeat_count, whose Default Value is set to "count(instance('bunnies')/bunnies_list/bunnies)".
The second question is our question list. Inside the question list is the repeat group. Its Repeat Count is set to the first question, "/data/repeat_count".
Inside the repeat group is a hidden value named position, with a Calculate Condition set to "position(..) + 1".
So the next question in the repeat group is another hidden value called bunny, with a Calculate Condition of "instance('bunnies')/bunnies_list/bunnies[current()/../position]/name".
The last question is a label to show that we have achieved what we set out to do. Its Display Text is "<output value="current()/../position" /> - <output value="current()/../bunny" />".
As a given path could point to multiple questions, we can use special functions to access a specific repeated value. Using something like /data/child_repeat/name outside of a repeat group will lead to an error.
XPath nodeset has more than one node [...] Cannot convert multiple nodes to a raw value. Refine path expression to match only one node.
To resolve, you must modify the expression so it gives a single value. Here are some common approaches:
Generally if a given question path can point to multiple results, you can use square brackets to filter that set of results or choose a specific answer by count.
When using repeat counts, it's possible to configure the form to "delete" repeat groups added if the count changes. (IE: Repeat count was originally 5 and is now 2)
For an example: Upload this XForm to CommCareHQ
In apps with user controlled repeat counts, sometimes a user will accidentally create a repeat group which is not needed.
Physically deleting a repeat group can create confusing or ambiguous behavior in a form, and potentially error prone, since the action can't be undone. By default the mobile app prevents users from creating repeat groups.
Instead, if that behavior is needed, it can be introduced by placing the questions inside of a repeat into a group, and using the group's Display Condition to eliminate the questions inside of the repeat. This lets users effectively cancel the repeat while also making it clear how to undo that action, and allowing the form itself to decide how to handle any awkwardness around removing the repeat data.