"XLSForm that looks up default values based on a selection" in a Repeat w/ Field-Lists

1. What is the issue?

Using the example of “XLSForm that looks up default values based on a selection” described Form Logic - ODK Docs, I added a second question (age) and I have no problem with the trigger/calculation populating the default when I wrap the questions in a field_list to get them on one page. However, if I place a repeat around it, then it appears that the trigger does not occur and the calculation does not populate the default. But if I remove the field_list while retaining the repeat, the trigger does seem to occur and the calculation populates the default.

I’m really confused because it works with field_list without a repeat and works with a repeat without a field_list.

2. What steps can we take to reproduce this issue?

Does not work:

selectlookuptest.xlsx (14.4 KB)

3. What have you tried to fix the issue?

Removing the field_list fixes this, but the desired behavior is to have both on same screen.

selectlookuptest-nofieldlist.xlsx (14.4 KB)

The triggers are happening and I can get it to work in Enketo using the following for the calculation:

instance('participants')/root/item[name=indexed-repeat(${participant}, ${repeat_group}, position(../..))]/phone_number

However, when I try to use it on the device (Connect) it gives me an error:

Error Occurred
Index 1 out of bounds for length 0

selectlookuptest-enketoworking.xlsx (14.5 KB)

Could this be a bug in Connect?

edit: I added this to an existing open issue on github.. maybe it's related:

Hi @jhogan

Thanks a lot for reporting the issue and for providing all the details that helped with the investigation. It seems to be a bug in ODK Collect, or more precisely in Javarosa, the library that powers the mobile app.

I need a bit more time to fully understand what is happening under the hood and to estimate how long it might take to fix. In the meantime, in case this issue is blocking you, here is a workaround you can use:

Instead of this:

instance('participants')/root/item[name=indexed-repeat(${participant}, ${repeat_group}, position(../..))]/phone_number

use this:

instance('participants')/root/item[name=current()/../../participant]/phone_number

It looks like the issue only occurs when the indexed-repeat function is used.

Hi again @jhogan,

After investigating this with the team, it turns out that the behavior you observed is not a bug in ODK Collect or JavaRosa, but rather the expected way that XPath relative references work inside predicates.

Why the original expression didn’t work

When using an expression like:

indexed-repeat(${participant}, ${repeat_group}, position(../..))

inside a predicate that queries an external instance, the call to position(../..) is evaluated relative to the external instance, not relative to your repeat group in /data. This is standard XPath behavior: in predicates, relative navigation is scoped to the node-set being filtered.

As a result, the position returned is not the position of the current repeat item, which is why the lookup fails.

Working alternatives

Both of the following expressions work because they explicitly anchor the position to the current node in the main instance:

Using the raw XPath path:

instance('participants')/root/item[name=current()/../../participant]/phone_number

Or keeping indexed-repeat but using current() to bind the position to the repeat context:

indexed-repeat(${participant}, ${repeat_group}, position(current()/../..))

Enketo behaves differently here because it implements parts of the XPath evaluation model slightly differently than JavaRosa. But in JavaRosa, the above behavior is correct and consistent.

Ok, thank you. I'm new to ODK and XPath and your explanation clarifies a lot for me. Thanks for looking into it.

John