You are reading O'Reilly XForms Essentials by Micah Dubinko. (What is this?) - Buy XForms Essentials Online

Repeating Line Items

One of the most sorely missed features in HTML forms comes by many names: tables, grid controls, or line items. The basic concept is that many forms in common use don't fit in well with a flat list of form controls. Figure 6.11, “Repeating line items in X-Smiles” shows one embodiment of this, rendered in the X-Smiles browser.

Another common scenario is a purchase order, with repeating lines, each containing quantity, description, price, and computed extended price.

The primary means of accomplishing this in XForms is the repeat element, which operates over homogeneous collections, the same as itemset, described earlier. The nodeset attribute of repeat selects a number of nodes, and the contents of the element, both from XForms and from the host language, are effectively repeated once for each resulting node. One way to think of this is to "unroll" the repeat, so that the following:

<repeat nodeset="item">
  <input ref="@quantity" .../>
</repeat>

has a similar effect, when the nodeset returns three item nodes, to:

<input ref="item[1]/@quantity" .../>
<input ref="item[2]/@quantity" .../>
<input ref="item[3]/@quantity" .../>

The main difference between a repeat element and unrolled syntax (besides convenience) is how the result is presented to the user. The repeat version makes it possible to show more items in a limited space, as in the case of scroll bars. Attributes of repeat further refine this by specifying how the initial appearance should be:

Items can be added and removed from repeat with the XForms Actions insert and delete, which are given as examples here and described fully in chapter Chapter 7, Actions and Events.

Every repeat has a current position, called an index. The concept of index extends the concept of focus, so that if a form control in a repeat has focus, then the item that contains that form control is the current index. The XPath function index( ) returns the current index of any repeat. This is useful for inserting at or removing the current item, as the following example shows:

<model>
  <instance>
    <items xmlns="">
      <item quantity="1" price="2.34"/>
    </items>
  </instance>
</model>
...
<repeat nodeset="items/item" id="r1">
  <input ref="@quantity" .../>
  <output ref="@price" .../>
</repeat>

<!-- insert just after the index item -->
<trigger>
  <label>Insert</label>
  <insert nodeset="/items/item" at="index('r1')" position="after"/>
  <setvalue ref="/items/item[index('r1')]/@quantity">0</quantity>
  <setvalue ref="/items/item[index('r1')]/@price">0.00</setvalue>
</trigger>

<!-- delete the index item -->
<trigger>
  <label>Delete</label>
  <delete ev:event="DOMActivate" nodeset="/items/item" at="index('r1')"/>
</trigger>

Newly inserted items are copied exactly from the last item in the repeat node-set. If the copied values aren't suitable, they can be overridden with setvalue, as in this example.