LC: Specify behavior of XPath-based actions when context node is deleted

It needs to be specified what happens to XPath-based actions whose context 
node has been deleted during execution of an action sequence.


1) A common use case would be a repeat construct in which the author is 
attempting to enforce a one repeat item minimum (i.e. a one row table).

2) Frequently, form authors desire the ability to put the trigger that 
deletes a repeat item within the repeat so that each repeat item receives 
a trigger capable of deleting that repeat item.

In a repeat construct having both of the properties above, it is tempting 
to write the trigger so that it first deletes the repeat item context node 
then, if the set of nodes over which the repeat operates is empty, then 
insert a new empty node.  To the end user, it appears as if the one 
remaining repeat item has been cleared rather than deleted.

While it is possible to achieve the effect just described by another 
means, a further clarification of the XForms specification is needed to 
indicate whether or not it can be done by the above means: delete followed 
by insert if empty.  The possible outcomes are

1) All further processing of an action sequence is terminated once the 
context node is destroyed.

I point this out only as a possibility, but I do not think this is 
desirable.  It would mean that a context node deletion is essentially 
illegal in XForms.  For example, a delete may be followed by a setfocus. 
Moreover, certainly the deferred update sequence must be executed after 
the deletion.

2) Absence of a context node for XPath-based actions like insert results 
in an xforms-binding-exception or a crash.

3) Absence of a context node for XPath-based actions like insert results 
in a "no-operation", similar to getting an empty nodeset or a false result 
on an 'if'.

This is what most implementers seem to be doing now.

4) The deletion of a context node removes it from the instance data, but 
not from availability for evaluation until after the action sequence is 
completed (immediately before deferred update behavior is executed).

This is the most desirable behavior.  Note that in XForms 1.1, delete has 
already been redefined so that the xforms-delete event has access to the 
deleted node(s) in its event context after the node has been removed from 
instance data. Moreover, it is possible to "parent up" from the deleted 
node to its former parent.

As a result of these facts, the following markup should work:

<repeat nodeset="a/b/c" id="R">
   ...
   <trigger>
      <label>Delete</label>
      <action ev:event="DOMActivate">
         <delete nodeset="." at="1"/>
         <insert if="count(../c)=0" context=".." 
origin="instance('proto')/c"/>
         <setfocus control="R"/>
      </action>
   </trigger>
</repeat>

The action sequence first deletes the repeat item containing the trigger 
that is activated.  Then, it attempts to use the context node to determine 
whether the parent has any 'c' children remaining.  If not, then the 
parent of the deleted node receives a new empty prototypical 'c' node.

For the record, this alternate body of markup that should already work in 
current implementations:

<repeat nodeset="a/b/c" id="R">
   ...
   <trigger ref="..">
      <label>Delete</label>
      <action ev:event="DOMActivate">
         <delete nodeset="c" at="index('R')"/>
         <insert if="count(c)=0" context="." 
origin="instance('proto')/c"/>
         <setfocus control="R"/>
      </action>
   </trigger>
</repeat>

By "misusing" the trigger ref to go to the parent 'b' element, the delete 
of a 'c' element does not delete the context node, so the subsequent 
insert, appropriately tweaked for the change of context, should achieve 
the desired effect.

The effect could also be achieved by first inserting a new prototypical 
'c' node if there is only one left, then setting the repeat index back to 
the one that should be deleted, then deleting afterward.

<repeat nodeset="a/b/c" id="R">
   ...
   <trigger>
      <label>Delete</label>
      <action ev:event="DOMActivate">
         <insert if="count(../c)=1" nodeset="../c" at="1" 
origin="instance('proto')/c"/>
         <setindex repeat="R" index="1"/>
         <delete nodeset="." at="1"/>
         <setfocus control="R"/>
      </action>
   </trigger>
</repeat>

Therefore, this isn't an issue of whether the desired user experience 
effect can be achieved but rather whether a seemingly natural way of 
expressing the solution should in fact work.

Best regards,
John M. Boyer, Ph.D.
STSM: Lotus Forms Architect and Researcher
Chair, W3C Forms Working Group
Workplace, Portal and Collaboration Software
IBM Victoria Software Lab
E-Mail: boyerj@ca.ibm.com 

Blog: http://www.ibm.com/developerworks/blogs/page/JohnBoyer

Received on Thursday, 12 April 2007 22:06:06 UTC