Previous chapter | Next chapter
So far, our form has gained in size and dependencies have been created. Now we need to develop some structure to present the form in an attractive and effective way, leading to a higher degree of conversion. As our forms grow in size, it becomes more and more desirable to display the form building block by building block.For instance, the form contains an Activities block, which we really only want to display once all data regarding the citizen or corporate applicant has been entered and validated.
Of course, there are several ways to do this. First (and not recommended) we could break the form into a number of sub forms and build some logic (using server-side programming) to let the submit action of the first sub form be followed by the display of the appropriate second sub form. This will soon lead to a hard-to-maintain situation and undermines the power of XForms. (Although XForms 2 will provide exactly this functionality). A better way is to use XForms power itself to navigate between blocks. This chapter will explain how we can build a wizard structure in our form using generic XForms features and will touch on the event mechanism integrated in XForms. The wizard features lean both on standard events as well as custom events, which we will raise in our code. What should the wizard do?
For now, we will not pay much attention to styling; we will however add some classes to our output, so that we can later use CSS technology to display the form in an attractive way. For now we will just use a standard styling provided with the XForms Engine of choice.
We will start with an empty form to build this wizard structure. Once this new form works as designed, we can merge the new techniques with our current events form.
We first add some new namespaces to our new XForms document:
xmlns:fst="http://some.url/xforms-state/1.0"
xmlns:exf="http://smartsite.nl/namespaces/xforms/1.0"
This last entry is necessary because we will use an extension function, valid(), that is not a part of the XForms 1.0 specification. As in this example we are using the Smartsite XForms engine, we embed this particular namespace.
We will use three instances.
the instance that holds the definition of the data to be collected, that we will now provide with the ‘data’ id for clarity. As the data instance is the first instance in the model, we should not have to modify the code referring to the instance. However, it is good practice to be explicit.
This is the instance containing the textual resources for the form. We create a section in the resources instance with resources for the navigational texts;
<xf:instance xmlns="" id="resources">
<text>
<!-- Wizard texts -->
<navigation>
<nextbutton>Next</nextbutton>
<previousbutton>Previous</previousbutton>
<steplist>Steps</steplist>
<step>Step</step>
<invalidcontrols>Not all values are valid and complete.</invalidcontrols>
<refreshscreen>Update values</refreshscreen>
<save>Save locally</save>
<load>Load locally</load>
<saved>Your data was saved successfully</saved>
<loaded>Your data was loaded successfully</loaded>
</navigation>
</text>
</xf:instance>
We will restructure the section for our control texts (i.e., labels, alerts, hints and help) in this instance from earlier versions of our form in order to be able to create a wizard-like presentation.
<steps>
<step>
....
</step>
<step>
....
</step>
</steps>
Within a step node, we then place the data for each of the controls that belong to this particular step. Here is where we group the controls over the different steps:
An individual step will look something like this:
<step>
<label>The first step</label>
<summary>The summary for the first step</summary>
<field name="name" >
<label>Your name</label>
<hint>Please enter your name.</hint>
<alert>This information is required.</alert>
<help>Your name could be 'John'</help>
</field>
<field name="email">
<label>Your email address</label>
<hint>Please enter your email address.</hint>
<alert>This information is required.</alert>
<help>Your email address could be 'John@acme.com'</help>
</field>
</step>
The field node is a generic node that may represent an input, select, textarea et cetera.
a new instance, formState, which is a helper instance containing information about the form and form state itself. We create the new instance named formState that will hold some data regarding the wizard: how many steps in our wizard, which is the current step, should we show previous and next buttons, and so on. The formState instance uses the fst: namespace declared earlier.
<xf:instance id="formstate">
<fst:state>
<fst:currentStep xsi:type="xsd:integer">0</fst:currentStep>
<fst:numberOfSteps xsi:type="xsd:integer">0</fst:numberOfSteps>
<fst:stepTitle xsi:type="xsd:string"/>
<fst:allSteps>
<fst:step nr="" />
</fst:allSteps>
<fst:previousButton />
<fst:nextButton />
</fst:state>
</xf:instance>
In the body of the document we now 'place' the controls, using a switch construct:
<xf:group class="container">
<xf:label class="formTitle" bind="steptitle"></xf:label>
<xf:switch>
<xf:case id="step_1">
<xf:setvalue ref="instance('formState')/fst:currentStep" value="1" ev:event="xforms-select" />
<xf:label>First step</xf:label>
<xf:input ref="name">
<xf:label ref="@label" />
<xf:hint ref="@hint" />
<xf:alert ref="@alert" />
<xf:help ref="@help"/>
</xf:input>
<xf:input ref="email">
<xf:label ref="@label" />
<xf:hint ref="@hint" />
<xf:alert ref="@alert" />
<xf:help ref="@help"/>
</xf:input>
</xf:case>
<xf:case id="step_2">
...
</xf:case>
<xf:switch>
</xf:group>
As you can see, a <xf:switch /> element is used here. Inside the switch we see a number of <xf:case /> elements. Intuitively, you will understand how this construct will work. We now have one group that will hold all controls; the switch/case construction will make sure we only will see the current section of the form. Inside each case is one of the groups with controls we had in the previous version of our form.
You see that we now refer to @label for the name node in the data instance. But this node does not initially have an attribute @label. We will copy these from the resources file. Its use will become manifest at a later point.
The resources instance also contains a template for a number of attributes:
<template label="-" hint="-" alert="-" help="-" step="-" />
We now add these attributes to the nodes in the data instance through these r
<!-- Decorate with default attributes and assign values;
we will use these attributes for the summary page -->
<xf:action while="//*[not(*) and not(@label)]">
<!-- Create the attributes for the last node that meets the condition -->
<xf:insert context="//*[not(*) and not(@label)]" origin="instance('resources')/template/@*" />
<!-- Assign values -->
<xf:setvalue ref="//*[not(*) and @label='-'][last()]/@label" value="instance('resources')/steps/step/field[@name=local-name(current()/..)]/label" />
<xf:setvalue ref="//*[not(*) and @hint='-'][last()]/@hint" value="instance('resources')/steps/step/field[@name=local-name(current()/..)]/hint" />
<xf:setvalue ref="//*[not(*) and @alert='-'][last()]/@alert" value="instance('resources')/steps/step/field[@name=local-name(current()/..)]/alert" />
<xf:setvalue ref="//*[not(*) and @help='-'][last()]/@help" value="instance('resources')/steps/step/field[@name=local-name(current()/..)]/help" />
<xf:setvalue ref="//*[not(*) and @step='-'][last()]/@step" value="count(instance('resources')/steps/step[field/@name=local-name(current()/..)]/preceding-sibling::step)+1" />
</xf:action>
The XForms insert instruction is very powerful. With it, we can extend an XML document or a node. We 'copy' the attributes onto all nodes in the data instance that do not yet have a @label attribute and then match on field names to collect the label, help, alert and hint texts from the resources instance and assign them to the new attributes in the data instance.
For our convenience, we also add an attribute @step and assign the number of the step a control is placed in, in that value.
As a result, our data instance is now enhanced to look like this:
<formData>
<name required="true" label="Your name" hint="Please enter your name." alert="This information is required." help="Your name could be 'John'" step="1" />
<email xsi:type="xf:email" required="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" label="Your email address" hint="Please enter your email address." alert="This information is required." help="Your email address could be 'John@acme.com'" step="1" />
<street required="true" label="Your street name" hint="Please enter the street name." alert="This information is required." help="The street name could be 'Broad Street'" step="2" />
<number required="true" label="Your building number" hint="Please enter your building number." alert="This information is required." help="The building number could be '27'" step="2" />
<postalcode required="true" label="Postal code" hint="Please enter the postal code." alert="This information is required." help="The postal code could be '1234 AB'" step="3" />
<city required="true" label="Your city name" hint="Please enter your city name." alert="This information is required." help="The city name could be 'London'" step="3" />
</formData>
One requirement for our wizard is that it will only allow us to go to the next step if the current step (and therefore all previous steps) have been completed. In order to validate the current step, we need to modify the logic that creates the required binding rule. The rule now applies to the current step in the wizard; we don’t want to be confronted with validations on inputs that we have not yet “reached” in our form.
<xf:bind nodeset="instance()//*[@step=instance('formstate')/fst:currentStep and @required='true']" required="true()" />
We also add some additional bindings to the model to determine some wizard state information, such as the title of the current step, a progress indicator, and whether or not we should show next and previous buttons:
<!-- Wizard logic -->
<!-- this line will create the Step 3|7 Steptitle indicator -->
<xf:bind id="steptitle"
nodeset="instance('formState')/fst:stepTitle"
calculate="concat(instance('resources')/navigation/step,' ', instance('formState')/fst:currentStep,'|', instance('formState')/fst:numberOfSteps,': ', instance('formState')/fst:allSteps/fst:step[@nr=instance('formState')/fst:currentStep])" />
<!-- These lines determine if the previous and next buttons can be displayed -->
<xf:bind nodeset="instance('resources')/steps/step"
readonly="@nr >= instance('formState')/fst:currentStep" />
<xf:bind id="previousButton"
nodeset="instance('formState')/fst:previousButton"
readonly="../fst:currentStep = 1" />
<xf:bind id="nextButton"
nodeset="instance('formState')/fst:nextButton"
readonly="../fst:currentStep >= ../fst:numberOfSteps" />
<xf:action ev:event="xforms-model-construct-done">
<!-- initialize steps state -->
<xf:setvalue ref="instance('formState')/fst:numberOfSteps" value="count(instance('resources')/steps/step)" />
<xf:toggle case="step_1" />
</xf:action>
The first of these binding rules will compose a line of text to be displayed on top of the active wizard page, saying something like “Step 1|3 : Your personal information” and will store this line in the formState instance (remember the only way to “store” state information is to keep it in an instance; we cannot use global variables to hold state information).
The second binding rule will set the XForms readonly property of all steps that meet the condition nr > currentStep to true and false to all others. In other words, all data in the steps “still ahead of us” in the wizard, will be read-only and thus not accessible. This will avoid error messages being raised because field validations fail.
The next two binding statements set the readonly property for the previous and next buttons: the previous button will be readonly if the current step is the first step in the wizard, and the next button will be readonly if the current step is the last step in the wizard.
Whenever a case is selected, the value of currentStep in the formState instance is set to its new value and an event is raised to update the dependencies.
Also we add a number of actions to the model. These actions will be executed when the XForms engine raises the xforms-model-construct-done event. In the first action, some formState information is distilled from the form structure contained in the resources instance.
The number of steps can be determined simply by counting the step elements in the instance. Also, just for good measure, we set the case to step_1. For this we use the <xf:toggle/> function.
The next code block is:
<!-- Complete the allSteps section in formState instance -->
<xf:action while="instance('formState')/fst:allSteps/fst:step[@nr='']">
<xf:insert context="instance('formState')/fst:allSteps" nodeset="node()" origin="instance('formState')/fst:allSteps/fst:step[last()]" if="count(instance('formState')/fst:allSteps/fst:step) < instance('formState')/fst:numberOfSteps" />
<xf:setvalue ref="instance('formState')/fst:allSteps/fst:step[@nr='']/@nr" value="count(./preceding::fst:step)+1" />
<xf:setvalue ref="instance('formState')/fst:allSteps/fst:step[.='']" value="instance('resources')/steps/step[position()=current()/@nr]/label" />
</xf:action>
This section will take the <allSteps/> section in the formState instance and populate it; it will continue to insert <step/> elements (from the template <step/> element) until the number of <step/> elements equals the number of steps and will set the nr attribute and the text value.
In other words, it will transform
<fst:allSteps>
<fst:step nr="" />
</fst:allSteps>
into:
<fst:allSteps>
<fst:step nr="1">The first step</fst:step>
<fst:step nr="2">The second step</fst:step>
<fst:step nr="3">The third step</fst:step>
</fst:allSteps>
In order to display buttons for previous and next below the form we add this block of code in the body of the form:
<!-- Previous / next buttons section -->
<xf:group class="previous_next_buttons">
<xf:trigger bind="previousButton" class="trg_left">
<xf:label ref="instance('resources')/navigation/previousbutton" />
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="instance('formState')/fst:previousButton" value="(instance('formState')/fst:currentStep)-1" />
<xf:setvalue ref="instance('formState')/fst:previousButton" value=".-1" while="not(exf:relevant(instance('resources')/texts/step[position()=current()])) and . >1" />
<xf:toggle>
<xf:case value="concat('step_',instance('formState')/fst:previousButton)" />
</xf:toggle>
</xf:action>
</xf:trigger>
<xf:trigger bind="nextButton" class="trg_right next_btn">
<xf:label ref="instance('resources')/navigation/nextbutton" />
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="instance('formState')/fst:nextButton" value="(instance('formState')/fst:currentStep)+1" />
<xf:setvalue ref="instance('formState')/fst:nextButton" value=".+1" while="not(exf:relevant(instance('resources')/texts/step[position()=current()])) and . <instance('formState')/fst:numberOfSteps" />
<xf:revalidate />
<xf:toggle if="exf:valid(instance()//*[@step=instance('formState')/fst:currentStep], false(),true())">
<xf:case value="concat('step_',instance('formState')/fst:nextButton)" />
</xf:toggle>
</xf:action>
</xf:trigger>
</xf:group>
The following section of code is also placed in the body of the form, following all groups for the form proper and the previous / next buttons. This block will display a list of links to the different sections of the form:
<!-- Group navigation -->
<xf:group class="steps_overview">
<xf:label ref="instance('resources')/navigation/steplist"></xf:label>
<xf:repeat nodeset="instance('formState')/fst:allSteps/fst:step">
<xf:trigger ref="." class="stepList-step trg_right" appearance="minimal" >
<xf:label>
<xf:output value="if(instance('formState')/fst:currentStep=@nr, concat('<span class=active>',@nr,' ', .,'</span>'), concat('<span>',@nr,'</span> ', .))" />
</xf:label>
<xf:action ev:event="DOMActivate">
<xf:toggle>
<xf:case value="concat('step_',@nr)" />
</xf:toggle>
</xf:action>
</xf:trigger>
</xf:repeat>
<xf:group class="action_buttons">
<xf:submit submission="tussentijdsOpslaan" class="trg_down tussentijdsOpslaan">
<xf:label ref="instance('resources')/navigation/save"></xf:label>
<xf:message ev:observer="tussentijdsOpslaan" ev:event="xforms-submit-done" level="ephemeral" ref="instance('resources')/navigation/saved"></xf:message>
</xf:submit>
<xf:submit submission="vorigeDataLaden" class="trg_left vorigeDataLaden">
<xf:label ref="instance('resources')/navigation/load"></xf:label>
</xf:submit>
</xf:group>
</xf:group>
Our total form contains less than 280 lines of code, including resource strings and inline comments. It provides smart navigation from page to page of the wizard, offers a step indicator and performs validation only on controls on the current page. The form as described here can be seen in action here
Previous chapter | Next chapter
<?xml version="1.0" encoding="UTF-8"?> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:fst="http://some.url/xforms-state/1.0" xmlns:exf="http://smartsite.nl/namespaces/xforms/1.0" > <head> <title>A Wizard Structue</title> <xf:model> <xf:instance xmlns="" id="data"> <formData> <name required="true" /> <email xsi:type="xf:email" required="true" /> <street required="true" /> <number required="true" /> <postalcode required="true" /> <city required="true" /> </formData> </xf:instance> <xf:bind nodeset="name" required="true()" /> <xf:instance xmlns="" id="resources"> <text> <!-- Wizard texts --> <navigation> <nextbutton>Next</nextbutton> <previousbutton>Previous</previousbutton> <steplist>Steps</steplist> <step>Step</step> <invalidcontrols>Not all values are valid and complete.</invalidcontrols> <refreshscreen>Update values</refreshscreen> <save>Save locally</save> <load>Load locally</load> <saved>Your data was saved successfully</saved> <loaded>Your data was loaded successfully</loaded> </navigation> <steps> <step> <label>The first step</label> <summary>The summary for the first step</summary> <field name="name" > <label>Your name</label> <hint>Please enter your name.</hint> <alert>This information is required.</alert> <help>Your name could be 'John'</help> </field> <field name="email"> <label>Your email address</label> <hint>Please enter your email address.</hint> <alert>This information is required.</alert> <help>Your email address could be 'John@acme.com'</help> </field> </step> <step> <label>The second step</label> <summary>The summary for the second step</summary> <field name="street" > <label>Your street name</label> <hint>Please enter the street name.</hint> <alert>This information is required.</alert> <help>The street name could be 'Broad Street'</help> </field> <field name="number" > <label>Your building number</label> <hint>Please enter your building number.</hint> <alert>This information is required.</alert> <help>The building number could be '27'</help> </field> </step> <step> <label>The third step</label> <summary>The summary for the third step</summary> <field name="postalcode" > <label>Postal code</label> <hint>Please enter the postal code.</hint> <alert>This information is required.</alert> <help>The postal code could be '1234 AB'</help> </field> <field name="city" > <label>Your city name</label> <hint>Please enter your city name.</hint> <alert>This information is required.</alert> <help>The city name could be 'London'</help> </field> </step> </steps> <template label="-" hint="-" alert="-" help="-" step="-" /> </text> </xf:instance> <!-- Wizard supporting data --> <xf:instance id="formState"> <fst:state> <fst:currentStep xsi:type="xsd:integer">0</fst:currentStep> <fst:numberOfSteps xsi:type="xsd:integer">0</fst:numberOfSteps> <fst:stepTitle xsi:type="xsd:string"/> <fst:allSteps> <fst:step nr="" /> </fst:allSteps> <fst:previousButton /> <fst:nextButton /> </fst:state> </xf:instance> <!-- Required fields only on active tab --> <xf:bind nodeset="instance()//*[@step=instance('formState')/fst:currentStep and @required='true']" required="true()" /> <!-- Wizard logic --> <!-- this line will create the Step 3|7 Steptitle indicator --> <xf:bind id="steptitle" nodeset="instance('formState')/fst:stepTitle" calculate="concat(instance('resources')/navigation/step,' ', instance('formState')/fst:currentStep,'|', instance('formState')/fst:numberOfSteps,': ', instance('formState')/fst:allSteps/fst:step[@nr=instance('formState')/fst:currentStep])" /> <!-- These lines determine if the previous and next buttons can be displayed --> <xf:bind nodeset="instance('resources')/steps/step" readonly="@nr >= instance('formState')/fst:currentStep" /> <xf:bind id="previousButton" nodeset="instance('formState')/fst:previousButton" readonly="../fst:currentStep = 1" /> <xf:bind id="nextButton" nodeset="instance('formState')/fst:nextButton" readonly="../fst:currentStep >= ../fst:numberOfSteps" /> <!-- Display a notification if any controls on the current page are invalid We are using the extension function valid() here. This function may vary between vendors. Here we use the Smartsite extension (hence the additional namespace in the header). The function checks all nodes on the active tab --> <xf:bind id="invalidControls" nodeset="instance('formState')/fst:stepIsValid" relevant="not(exf:valid(instance('data')//*[@step=instance('formState')/fst:currentStep], false(),true()))" /> <xf:action ev:event="xforms-model-construct-done"> <!-- Count the number of steps --> <xf:setvalue ref="instance('formState')/fst:numberOfSteps" value="count(instance('resources')/steps/step)" /> <!-- Start with the first block --> <xf:toggle case="step_1" /> <!-- Complete the allSteps section in formState instance --> <xf:action while="instance('formState')/fst:allSteps/fst:step[@nr='']"> <xf:insert context="instance('formState')/fst:allSteps" nodeset="node()" origin="instance('formState')/fst:allSteps/fst:step[last()]" if="count(instance('formState')/fst:allSteps/fst:step) < instance('formState')/fst:numberOfSteps" /> <xf:setvalue ref="instance('formState')/fst:allSteps/fst:step[@nr='']/@nr" value="count(./preceding::fst:step)+1" /> <xf:setvalue ref="instance('formState')/fst:allSteps/fst:step[.='']" value="instance('resources')/steps/step[position()=current()/@nr]/label" /> </xf:action> <!-- Decorate with default attributes and assign values; we will use these attributes for the summary page --> <xf:action while="//*[not(*) and not(@label)]"> <!-- Create the attributes for the last node that meets the condition --> <xf:insert context="//*[not(*) and not(@label)]" origin="instance('resources')/template/@*" /> <!-- Assign values --> <xf:setvalue ref="//*[not(*) and @label='-'][last()]/@label" value="instance('resources')/steps/step/field[@name=local-name(current()/..)]/label" /> <xf:setvalue ref="//*[not(*) and @hint='-'][last()]/@hint" value="instance('resources')/steps/step/field[@name=local-name(current()/..)]/hint" /> <xf:setvalue ref="//*[not(*) and @alert='-'][last()]/@alert" value="instance('resources')/steps/step/field[@name=local-name(current()/..)]/alert" /> <xf:setvalue ref="//*[not(*) and @help='-'][last()]/@help" value="instance('resources')/steps/step/field[@name=local-name(current()/..)]/help" /> <xf:setvalue ref="//*[not(*) and @summary='-'][last()]/@summary" value="instance('resources')/steps/step/field[@name=name(current()/..)]/summary" /> <xf:setvalue ref="//*[not(*) and @step='-'][last()]/@step" value="count(instance('resources')/steps/step[field/@name=local-name(current()/..)]/preceding-sibling::step)+1" /> </xf:action> </xf:action> <xf:submission id="send" method="post" action="http://xforms.smartsite.nl/xformsecho" ref="." serialization="multipart/form-data" validate="true" replace="none"> <xf:message ev:event="xforms-submit-error" if="event('error-type')='validation-error'">Validation failed.</xf:message> </xf:submission> </xf:model> </head> <body> <xf:group class="container"> <xf:label class="formTitle" bind="steptitle"></xf:label> <xf:switch> <xf:case id="step_1"> <xf:setvalue ref="instance('formState')/fst:currentStep" value="1" ev:event="xforms-select" /> <xf:label>First step</xf:label> <xf:input ref="name"> <xf:label ref="@label" /> <xf:hint ref="@hint" /> <xf:alert ref="@alert" /> <xf:help ref="@help"/> </xf:input> <xf:input ref="email"> <xf:label ref="@label" /> <xf:hint ref="@hint" /> <xf:alert ref="@alert" /> <xf:help ref="@help"/> </xf:input> </xf:case> <xf:case id="step_2"> <xf:setvalue ref="instance('formState')/fst:currentStep" value="2" ev:event="xforms-select" /> <xf:label>Second step</xf:label> <xf:input ref="street"> <xf:label ref="@label" /> <xf:hint ref="@hint" /> <xf:alert ref="@alert" /> <xf:help ref="@help"/> </xf:input> <xf:input ref="number"> <xf:label ref="@label" /> <xf:hint ref="@hint" /> <xf:alert ref="@alert" /> <xf:help ref="@help"/> </xf:input> </xf:case> <xf:case id="step_3"> <xf:setvalue ref="instance('formState')/fst:currentStep" value="3" ev:event="xforms-select" /> <xf:label>Third step</xf:label> <xf:input ref="postalcode"> <xf:label ref="@label" /> <xf:hint ref="@hint" /> <xf:alert ref="@alert" /> <xf:help ref="@help"/> </xf:input> <xf:input ref="city"> <xf:label ref="@label" /> <xf:hint ref="@hint" /> <xf:alert ref="@alert" /> <xf:help ref="@help"/> </xf:input> </xf:case> </xf:switch> <!-- Previous / next buttons section --> <xf:group class="previous_next_buttons"> <xf:trigger bind="previousButton" class="trg_left"> <xf:label ref="instance('resources')/navigation/previousbutton" /> <xf:action ev:event="DOMActivate"> <xf:setvalue ref="instance('formState')/fst:previousButton" value="(instance('formState')/fst:currentStep)-1" /> <xf:setvalue ref="instance('formState')/fst:previousButton" value=".-1" while="not(exf:relevant(instance('resources')/texts/step[position()=current()])) and . >1" /> <xf:toggle> <xf:case value="concat('step_',instance('formState')/fst:previousButton)" /> </xf:toggle> </xf:action> </xf:trigger> <xf:trigger bind="nextButton" class="trg_right next_btn"> <xf:label ref="instance('resources')/navigation/nextbutton" /> <xf:action ev:event="DOMActivate"> <xf:setvalue ref="instance('formState')/fst:nextButton" value="(instance('formState')/fst:currentStep)+1" /> <xf:setvalue ref="instance('formState')/fst:nextButton" value=".+1" while="not(exf:relevant(instance('resources')/texts/step[position()=current()])) and . <instance('formState')/fst:numberOfSteps" /> <xf:revalidate /> <xf:toggle if="exf:valid(instance()//*[@step=instance('formState')/fst:currentStep], false(),true())"> <xf:case value="concat('step_',instance('formState')/fst:nextButton)" /> </xf:toggle> </xf:action> </xf:trigger> </xf:group> </xf:group> <!-- Group navigation --> <xf:group class="steps_overview"> <xf:label ref="instance('resources')/navigation/steplist"></xf:label> <xf:repeat nodeset="instance('formState')/fst:allSteps/fst:step"> <xf:trigger ref="." class="stepList-step trg_right" appearance="minimal" > <xf:label> <xf:output value="if(instance('formState')/fst:currentStep=@nr, concat('<span class=active>',@nr,' ', .,'</span>'), concat('<span>',@nr,'</span> ', .))" /> </xf:label> <xf:action ev:event="DOMActivate"> <xf:toggle> <xf:case value="concat('step_',@nr)" /> </xf:toggle> </xf:action> </xf:trigger> </xf:repeat> <xf:group class="action_buttons"> <xf:submit submission="tussentijdsOpslaan" class="trg_down tussentijdsOpslaan"> <xf:label ref="instance('resources')/navigation/save"></xf:label> <xf:message ev:observer="tussentijdsOpslaan" ev:event="xforms-submit-done" level="ephemeral" ref="instance('resources')/navigation/saved"></xf:message> </xf:submit> <xf:submit submission="vorigeDataLaden" class="trg_left vorigeDataLaden"> <xf:label ref="instance('resources')/navigation/load"></xf:label> </xf:submit> </xf:group> </xf:group> <noscript> <xf:trigger> <xf:label>Update screen</xf:label> <xf:hint>Scripting is off. Update the screen by clicking this button.</xf:hint> </xf:trigger> </noscript> <!-- <xf:submit class="block-ui sync"> <xf:label>Submit</xf:label> <xf:message ev:observer="send" ev:event="xforms-submit-done" level="ephemeral">Submit ready...</xf:message> </xf:submit> --> </body> </html>