Conditional action handlers

The latest Working Draft of XForms 1.1 provides support for a number of new attributes on action to allow for conditional execution and looping. These attributes have been implemented in formsPlayer since version 1.4.3, and this section explains how to use them.

The if attribute

The if attribute indicates that an action block should only be executed if the XPath expression used evaluates to true().

For example, if our application was looping through a list of RSS feeds to process, once it reaches the end of the list we would set the feed counter back to 1 and save any new items we had read.

The following mark-up tests for a loop counter to be greater than the count of the number of RSS feeds to process, and when it is, it resets the counter and invokes a submission:

<xf:action if="@i &gt; count(feed)">
  <xf:setvalue ref="@i" value="1" />
  <xf:send submission="sub-put-unread-items" />
</xf:action>

For more information and further examples see Section 3.4 of XForms 1.1.

The iterate attribute

The iterate attribute allows our applications to repeatedly perform the same action across a nodeset.

For example, an application that has retrieved data from an RSS feed may need to convert any dates from RFC 288 format to ISO 8601 format (the latter is used by XML Schema and therefore XForms). Also, since pubDate is optional in RSS feeds, the appication may need to create a default date if none is provided. It could do this by looping through all of the returned items and then either adding or converting pubDate.

The first step is to create a handler that will execute when the RSS feed has been loaded successfully:

<xf:action ev:observer="sub-get-rss-feed" ev:event="xforms-submit-done">

Next we set up an iterator across each of the items in the loaded RSS channel:

  <xf:action iterate="instance('inst-current-feed-rs')/channel/item">

The action handler then checks that there is actually a pubDate element, and if there is, it is converted to ISO 8601 format:

    <xf:action if="*[local-name()='pubDate']">
      <xf:setvalue ref="*[local-name()='pubDate']"
        value="inline:convrfc288datetoiso8601(.)" />
    </xf:action>

Note that the evaluation context within this action handler will be each of the nodes of the nodeset, in turn. However, since @if itself does not change the evaluation context, the setvalue action is actually being evaluated in the context of the action with the iterate attribute, and not the action with the if attribute.

There is no way to add an 'else' branch, so if we want to perform some action if there is no pubDate element, we have to repeat our previous test and use not(). If no pubDate element is present then the following code simply creates a new pubDate element and then sets it to a default value of the current date and time:

    <xf:action if="not(*[local-name()='pubDate'])">
      <xf:duplicate origin="instance('inst-control')/templates/pubDate"
        ref="." />
      <xf:setvalue ref="*[local-name()='pubDate']" value="now()" />
    </xf:action>
  </xf:action>
</xf:action>

For more information and further examples see Section 3.5 of XForms 1.1.