XForms repeat - ask user for number of rows

949 Views Asked by At

I'd like to ask the user via an xf:input, "how many items" do you want in a certain repeat, and then, based on the user's response, present a repeat with that number of rows (which I guess would also be generated in the xf:instance).

I'm familiar with using triggers to insert and delete rows.

What I'm asking here is how to prompt the user for the initial number of rows to be created and presented.

For example, suppose the user wanted 10 rows. I guess the repeated data could be made not relevant, until they'd answered that question, and when they answer it, some action could dynamically create the rows, and then make the repeated data relevant.

But how to do this?

Update 20 Dec

The markup below presents an input field, and in betterform, does add rows to the instance when the button is pressed. However, betterForm doesn't display the data in the UI (though I guess this is strictly a separate question). I haven't tried other implementations.

<xhtml:head>
    <xf:model id="m">
        <xf:instance id="main-instance">
          <data>
            <items>
              <item1>item 11</item1>
              <item2>item 12</item2>
              <item3>item 13</item3>
            </items>
          </data>
          </xf:instance>


        <xf:instance id="variable">
            <variable xmlns="">
                <iteration-count/>
            </variable>
        </xf:instance>
    </xf:model>
</xhtml:head>
<xhtml:body>

  <!-- Simple field to enter the number of iterations -->
  <xf:input ref="instance('variable')/iteration-count">
    <xf:label>How many iterations?</xf:label>
  </xf:input>

  <xf:trigger>
    <xf:label>Insert with while</xf:label>
    <xf:action ev:event="DOMActivate">
            <xf:action while="instance('variable')/iteration-count != 0">
                <xf:insert ev:event="DOMActivate" nodeset="items"/>
                <xf:setvalue ref="instance('variable')/iteration-count" value=". - 1"/>
            </xf:action>
          <!--<xf:refresh model="m"/>-->
        </xf:action>
    </xf:trigger>


  <table>

    <tbody id="r-attrs" xf:repeat-nodeset="items">
      <tr>
        <td>
          <xf:output ref="item1"></xf:output>
        </td>
        <td>
          <xf:output ref="item2"></xf:output>
        </td>
        <td>
          <xf:output ref="item3"></xf:output>
        </td>
      </tr>
    </tbody>

  </table>       

</xhtml:body>

3

There are 3 best solutions below

0
On

The while and the iterate attributes of an action will repeat this action.

2
On

You are asking two separate questions if I understand well:

  1. how to prompt the user
  2. how to insert the given number of iterations

Here are

  1. To prompt the user, you could use a dialog (xforms:dialog is standardized by XForms 2.0, and Orbeon Forms supports the xxforms:dialog extension). You could also use an xforms:switch to show/hide the field vs. the repeat.

  2. Once the value is obtained, you can, as Alain says, the use the while or the iterate attribute to perform the insert action a number of times. Or, if you have XPath 2.0, you can use a for in the origin expression.

Here is a full example that shows two ways of inserting:

<xh:html xmlns:xh="http://www.w3.org/1999/xhtml"
      xmlns:xf="http://www.w3.org/2002/xforms"
      xmlns:ev="http://www.w3.org/2001/xml-events">
    <xh:head>
        <xf:model>
            <xf:instance id="repeats">
                <repeats/>
            </xf:instance>
            <xf:instance id="count">
                <count/>
            </xf:instance>
        </xf:model>
    </xh:head>
    <xh:body>

        <!-- Simple field to enter the number of iterations -->
        <xf:input ref="instance('count')">
            <xf:label>How many iterations?</xf:label>
        </xf:input>

        <!-- Variant 1 -->
        <xf:trigger>
            <xf:label>Insert with for</xf:label>
            <xf:insert
                ev:event="DOMActivate"
                if="instance('count') castable as xs:integer"
                context="instance('repeats')"
                ref="repeat"
                origin="for $i in 1 to instance('count')
                        return xf:element('repeat')"/>
        </xf:trigger>

        <!-- Variant 2 -->
        <xf:trigger>
            <xf:label>Insert with while</xf:label>
            <xf:insert
                ev:event="DOMActivate"
                if="instance('count') castable as xs:integer"
                context="instance('repeats')"
                while="count(repeat) lt xs:integer(instance('count'))"
                ref="repeat"
                origin="xf:element('repeat')"/>
        </xf:trigger>

        <xf:trigger>
            <xf:label>Clear</xf:label>
            <xf:delete ev:event="DOMActivate" ref="instance('repeats')/*"/>
        </xf:trigger>

        <!-- Your repeat -->
        <xf:repeat ref="repeat">
            <xf:output value="position()"/>
        </xf:repeat>

    </xh:body>
</xh:html>
0
On

One way to get the information from the user is to bind an input widget to an element in an ancillary document (I often call that document ui since it holds user-interface status info) and controlling insertion and display from that. If you want to force the user to provide a value, you can make it default to 0; if you don't, you can provide some plausible default number which the user can override if they wish.