Bump.
So @ln, @martijnr, what more do we need to do here to push a spec out for add/remove repeat instance event? [other than me keeping my mouth shut. ] Not sure we necessarily need a call, but I'm certainly available if y'all think it'd help.
Bump.
So @ln, @martijnr, what more do we need to do here to push a spec out for add/remove repeat instance event? [other than me keeping my mouth shut. ] Not sure we necessarily need a call, but I'm certainly available if y'all think it'd help.
I believe the example in the XForms 1.1 spec is entirely unhelpful and the cause of the confusion.
IBM has a clear example, simplified (by me) as this:
<data>
<expenses>
<row>
<detail/>
</row>
</expenses>
<other>
<location/>
</other>
</data>
<repeat nodeset="expenses/row">
...
<setvalue event="odk-new-repeat" ref="/data/other/location" value="context()/detail" />
</repeat>
So this use case for context()
is exactly as:
Now suddenly, the description of context() in the W3C spec makes sense to me:
In the example above the Single Node Binding (= ref
) is outside the repeat and therefore not relative to the repeat context node.
So, I think we can continue to choose to not cater to this use case for now, and proceed with our plan. And we can add context()
when we feel it is necessary.
A less happy realization after understanding ref
vs. nodeset
better is that I think we're doing ref
values for elements inside a repeat (in the body) incorrectly. I think these should always have a relative path instead of an absolute path (as in XForms spec). Maybe not worth correcting as this point (and a much less serious deviation than the one that was recently corrected in pyxform).
Only <setlocation>
is asynchronous, right, or am I misunderstanding? The other events+actions seem synchronous (and predictable). I think it will be quite intuitive for form designers to avoid issues with <setlocation>
.
I'm on board. I still think this event should have a custom name because it won't be as broad as the W3C XForms one, agreed? I'll give a bit for others (@Xiphware) to chime in but if all that is sounding reasonable, I will write this up in as much detail as I can including when the event is dispatched relative to calculate evaluation, context rules, etc.
Yes, that's sounding right. I think that was in fact the case that was tripping me up and that led me to cross out all that stuff in red above. It all adds up now, thanks. I agree that this is a wrong we can live with.
Yes agreed. I actually cannot figure out if a repeat is supposed to trigger the W3C xforms-insert
event.
In W3C XForms, repeat instances can only be added by triggering the insert
action. That could be by using a trigger
control, responding to some event, whatever. On the other hand, in ODK XForms, repeats are added by client pixie dust. I suppose one could say that the insert
action is implicit and that there is no explicit definition of what should trigger it (preferably a confusing dialog).
Oh, and in ODK XForms, trigger
is ?!?!?!(or more generously, a specialized case of the W3C XForms one).
Ah right, that makes sense. Thanks! So I am still in favor of odk-new-repeat then.
Yes, we don't talk about that ;).
As promised, here is a more formal spec proposal to summarize our discussion:
The odk-new-repeat
event is dispatched when a new instance of a repeat is added to the primary instance and before recomputation of calculate
s, constraint
s, etc. Actions triggered by odk-new-repeat
must be nested in the repeat form control.
Unlike W3C XForms' xforms-insert
:
repeat
control (W3C XForms identifies the added nodes and the insert position in event context information so handlers can go anywhere)insert
action may insert multiple nodes)I was going to also write up some text to describe that the event
attribute on actions can specify multiple events but now I'm not so sure about it. Does the following which I showed here make sense?
<repeat nodeset="/data/friends">
<setvalue event="odk-new-repeat odk-instance-first-load" ref="age" value="../../my_age + 2" />
<input ref="/data/friends/age">
<label>Update age</label>
</input>
...
</repeat>
That is, should a handler for odk-instance-first-load
be allowed to be nested in a repeat
control? The ODK XForms spec currently says no: "Action elements triggered by initialization events go in the model as siblings of bind nodes. Action elements triggered by control-specific events are nested in that control block."
It's not a problem for pyxform
or other form builders to generate two separate setvalue
events, certainly. But it does feel unfortunate.
Am I understanding correctly that it's by requiring that the action be nested in the control that the evaluation context is the new repeat instance? If we did allow top-level handlers, we'd have to say that the evaluation context for the ref
is the new repeat and we'd have to add something like "relative expressions for ref
are only allowed in nested handlers." That does not seem very elegant. I believe that's what Dimagi did with the jr-insert
event.
In W3C XForms, my understanding is that handlers of xforms-insert
can be siblings of binds. In that case, the evaluation context would be the primary instanceโs single child. However, one could use the index()
function or the event properties that the context is augmented with in the single node binding expression (the ref
).
Thanks for the great writeup!
All seems workable to me, but I'd probably want to re-read it all again in the light of the day (1am presently) to make sure there arent any potential gotchas we've missed...
Thanks a lot @LN!
Yes, I think that's okay.
Yes, indeed. Good idea to use the index() function to circumvent the Single Node Binding limitation of <setvalue>
. So we'd have to add one of these <setvalue>
elements for all default (defined in XForm) repeat instances (and add id
attributes in the model?). I don't think it's worth it. The rationale is to avoid the deviation of our own ODK XForms spec only, right?
In your XForm snippet we have ref="age"
and ref="/data/friends/age"
. As discussed, the first is likely the only correct ref, but so far we're only using the second (due to pyxform). So in this case, let's use the consistently incorrect /data/friends/age
. (Tbh, it also avoids having to bring back relative ref support in Enketo, though would be open to that if it deemed worthwhile ;)).
(PS. am away for next 10 days so apologies in advance for late responses)
All looks good to me. Just want to double-check that all this will properly fire (potentially multiple times) when you first open a form and the repeat_count expression is evaluated for the very first time, eg
<bind calculate="count( /indefinite-repeat/person )" nodeset="/indefinite-repeat/count" type="string"/>
<bind calculate="if ( /indefinite-repeat/count = 0 or /indefinite-repeat/person[position()= /indefinite-repeat/count ]/choice != 'a', /indefinite-repeat/count + 1, /indefinite-repeat/count )" nodeset="/indefinite-repeat/person_count" readonly="true()" type="string"/>
...
<repeat jr:count=" /indefinite-repeat/person_count " nodeset="/indefinite-repeat/person">
ie it requires multiple passes thru binding calculations to get the final repeat count. I assume the existing mechanism for populating the additional repeat nodesets already handles this, so it's a given, but that's the best conceivable gothca I could come up with...
You could have populated //person/choice
with value 'a' via setvalue triggered by odk-new-repeat
to really mess with our heads.
I think your example should be fine as far as I can see (as long as setvalue doesn't create an infinite repeat-count-update loop, which would be a form design issue).
Actually the example was courtesy @ln, not one of my devious machinations ...
indefinite-repeat.xlsx (6.5 KB)
Thanks so much, all. I will attempt to have a spec PR soon.
That's a great thing to call out, thank you.
Let us all deviously machinate together.