Different behaviour between Enketo and ODK Collect on trigger and instance

Dear community,

I am working on a form that ultimately will be used to create and update entities containing data about farm fields.

The attached test form retrieves data for testing purposes from an external csv "unites_agricoles_entity.csv". This csv will later be replaced by the entity.

If a new entity is to be created, the user can or cannot retrieve data from other csv files, containing previously collected basic information about the farms (exploitations_agricoles.csv) and related fields (unites_agricoles.csv). In its final state the form will be used to capture polygones and other detailed infromation about each field.

I encounter problems with fields that are either prefilled with instance for updating or used to enter data when a new entity is created from scratch.

The problems only occur in ODK Collect not in Enketo. In the attachd form one such field is included as an example in two variations (instance_with_trigger and instance_without_trigger).

While both work well in Enketo these errors occur in ODK Collect:

  • the field "instance_with_trigger" is not retrieving data at all from the external csv as expected, although the trigger is on the field "ua_code" containing the relevant data.
  • the field "instance_without_trigger" is retrieving data from the external csv as expected. But when creating a new submission without retrieving external data, the field is emptied each time upon submission of the form.

The issue seams to be related with trigger. The "instance_with_trigger" would be my preffered solution, but somehow the trigger on "ua_code" does not initiate retrieving data with instance from the csv as I would expect. Why?

Thanks for any help.

instance_test.xlsx (568.3 KB)
exploitations_agricoles.csv (300 Bytes)
unites_agricoles_entity.csv (12.7 KB)
unites_agricoles.csv (492 Bytes)

This is possibly because your triggering control ua_code is being set with a calculation, as opposed to the control actually responding to a user-initiated value change?

The formal XForms spec isnt explicit about it, but does say:

Dispatched in response to: a change to an instance data node bound to a core form control.

which doesn't really distinguish between a change in response to a control due to user input vs an 'automated' change due to a calculation associated with that control... Perhaps @LN know's whether Collect also fires these xforms-value-changed events after (just) re-calculating a node? Perhaps Collect vs Enketo differ slightly in this regard...

It might be worth trying to move it all under your actual triggers: ua_choix_carte_1, ua_choix_liste_1, ua_choix_carte_2, ..., instead of your ostensibly consolidated trigger (ie the ua_code calculation). Although this cant readily be expressed in XLSForm and would require manually tweaking the raw XML XForm [as discussed here: Using two variable names under the Trigger column - #9 by ahblake]

Thanks @Xiphware for your suggestions. Manually changing the the XForm is too complex for my currect level unfortunately :exploding_head: !

After some headaches and a long night, I found a solution suggested by @LN in this closely related thread: 'Trigger' a calculation at the 'end' of a form - #4 by LN

Enclosing the the calculation with once() triggers pulling the values from the external csv reliably also in Collect and allows changing the retrieved value or entering a new value when the field is not prepopulated from the csv.

The major disadvantage of this solution is that once the calculation has been carried out, it can no longer be changed by going back to a previous field and changing its value.

instance_test.xlsx (567.5 KB)

Another workaround is to insert a user filled question ("verify_code") serving as a trigger for the calculation (although this question does not make much sense otherwise). This will recalculate the field if the new question is reset. This solution is not perfect but a robust workaround.

To make this solution more reliable, it would be ideal if the trigger question ‘verify_code’ were set to empty each time it is opened to ensure that the calculation in the following field ‘producteur_nom’ is triggered again. I have not managed to achieve this yet. Does anybody have a suggestion?

instance_test_2.xlsx (567.9 KB)

Finally: The prefered solution would be if also Collect accepts trigger on calculated fields. @LN: would this be something that is considered in future versions?

1 Like

This requirement makes a lot of sense and I'm sure it's relatively common. Sometimes there are multiple possible sources of existing data and you want your users to be able to select from any of them and then use the selected value in a unified way for the rest of the form.

Collect interprets the spec that @Xiphware points to as saying that only user-provided values being able to trigger actions. I'm surprised to find out that Enketo does differently and I've made a note of it in the differences doc for reference.

At first glance, it doesn't look like triggering the event when a calculation on a field that is bound to a control runs would be straightforward in Collect. I wonder whether instead we could consider making the trigger column accept a space-separated list of references.

This can make form updates really difficult to get right so it's best avoided if possible.

If trigger accepted a list of references, pyxform could create those several setvalue actions.

1 Like

The current restriction in XLSForm of only permitting a single trigger is definitely a limitation that seems to be popping up now and then. Although not as general as what's permitted in XForms itself (eg multiple different triggers with different calculations), I think adding a space-separated list of references as a trigger(s) is a great idea to address the gap. And well in keeping with how XLSForm triggers are defined today.

:+1:

1 Like

Thanks, @Xiphware! You're right that it's been coming up recently.

@Lindsay_Stevens_Au would love to hear your thoughts. It feels like something that could unlock quite a bit of power without much risk, the very best kind of addition!

Here are some precedents for having lists in XLSForm:

  • appearances are space-separated
  • parameters accept spaces, semicolons or commas as delimiters. We document commas.

I don't feel picky about the separator. I find commas are easiest to read. I'd prefer not to have multiple ways to do the same thing as with parameters but don't feel strongly about it.

1 Like

Yes parsing them as CSV like parameters probably easiest. I am wondering what kinds of things users would build on top of this and as a result what are the next things that they would ask for, to consider in the design now.

One that comes to mind is to have the trigger act based on some expression involving multiple fields. Is that possible / plausible? If so maybe it would make sense to require users to write the list as an expression e.g. ${q1} or ${q2} to reflect the current behaviour, and establish forward-compatible syntax for things like ${q1} and (${q2}) or ${q3} as if to say "trigger the change after q1 is updated and either q2 or q3 are updated". Maybe weird maybe useful?

I am also wondering if this feature prompts a need to detect and warn about reference cycles in these trigger conditions? Or is that better to do in javarosa / webforms? Are there any other pitfalls lurking in this feature that pyxform could help users avoid?

1 Like

This doesn't really map to how the underlying xforms-value-changed event works. Triggering an event when one 'instance data node (bound to a core form control) changes' and a completely diffferent 'instance data node (bound to a different core form control) changes', would seem to entail remembering when the user changes something, and then should some other question subsequently be changed - at some indefinite time in the future - then going back and performing the trigger... I imagine the book-keeping would be a nightmare :slight_smile:

At most, I think there could be the potential - in the future - to fire xforms-value-changed events on form control-bound instance elements should they also happen to change as a result of a (re) calculation, as Enketo appears to be doing.

Other than that, the only thing that stands out to me in terms of what XLSForm trigger's cannot express - but an XForm can - is associate different calculations with different source triggers. But as defined today, I suspect trying to extend the current XLSForm model to accommodate this would probably require a significant redesign.

1 Like

For our use case this would move things into the right direction. I cannot comment on the technical details, but what is as important as having trigger able to point to more than one field, is that trigger also works with calculated fields. Just as @LN points out

1 Like

I think there is probably some good 'robust conversation' :wink: to be had around whether xform-value-changed events should/not be triggered by calculations; not the least of which is that you can potentially have a cascade of dependent calculations, all potentially triggering something, which could then be a trigger for something else... oh my! :face_with_spiral_eyes: [FWIW I certainly agree with the potential usefulness, but the implementation implications of getting it right appear non-trivial]

But independent of all that, I think there is still a fairly clear benefit - and relatively non-controversial risk - to being able to specify multiple (comma-separated?) triggering UI controls. Indeed, this is just exposing - in XLSForm - what you can already write in XForms today.

1 Like

Thanks for the good questions, @Lindsay_Stevens_Au!

I agree with @Xiphware that the and case isn't expressible in the ODK XForms spec now and is unlikely to be.

Also agree with @Xiphware that the limitation that only user-modified values can trigger actions prevents cycles. I think we can rely on that for now even knowing that Enketo deviates from that.

@Lindsay_Stevens_Au unless you feel strongly about using spaces as the separator, let's go with commas (optionally followed by whitespace). I think that's most people's default when expressing a list.

Issue at https://github.com/XLSForm/pyxform/issues/733

1 Like