Search code examples
orbeonxforms

How to use input fields with date format that can be null in a relevant rule of another element?


I have a simple form, with some date fields and radio buttons. One of the element has a relevant rule that depends on two previous questions, one of them is a radio button, and the other one is a date field. I have created a simple example here that can be run directly in Orbeon:

    <xh:html xmlns:xh="http://www.w3.org/1999/xhtml"
     xmlns:ev="http://www.w3.org/2001/xml-events"
     xmlns:exf="http://www.exforms.org/exf/1-0"
     xmlns:fb="http://orbeon.org/oxf/xml/form-builder"
     xmlns:fr="http://orbeon.org/oxf/xml/form-runner"
     xmlns:saxon="http://saxon.sf.net/"
     xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
     xmlns:sql="http://orbeon.org/oxf/xml/sql"
     xmlns:xf="http://www.w3.org/2002/xforms"
     xmlns:xi="http://www.w3.org/2001/XInclude"
     xmlns:xs="http://www.w3.org/2001/XMLSchema"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:xxf="http://orbeon.org/oxf/xml/xforms"
     xmlns:xxi="http://orbeon.org/oxf/xml/xinclude">
    <xh:head>
    <xh:meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
    <xh:title>DateTest</xh:title>
    <xf:model id="fr-form-model" xxf:expose-xpath-types="true">
        <xf:instance xxf:readonly="true" id="fr-form-metadata" xxf:exclude-result-prefixes="#all">
        <metadata>
            <application-name>WebForms</application-name>
            <form-name>DateTest_v1</form-name>
            <title xml:lang="en">DateTest</title>
            <description xml:lang="en">Version: 1. Modification date: 2015-05-27 14:43:59. Publication date: 2015-05-27 14:43:59.</description>
            <singleton>false</singleton>
        </metadata>
        </xf:instance>
        <xf:instance id="fr-form-instance">
        <form>
            <Category>
            <Question1/>
            <Question2/>
            <Question3/>
            <Question4/>
            </Category>
        </form>
        </xf:instance>
        <xf:bind id="fr-form-binds" ref="instance('fr-form-instance')">
        <xf:bind id="Category-bind" name="Category" ref="/form/Category">
            <xf:bind id="Question1-bind" name="Question1" ref="Question1" required="true()"/>
            <xf:bind id="Question2-bind" name="Question2" readonly="false" ref="Question2"
                 relevant="(( $Question1='Answer' ) )"
                 required="true()"
                 type="xf:date"/>
            <xf:bind id="Question3-bind" name="Question3" readonly="false" ref="Question3"
                 relevant="(( $Question1='Answer' and string-length($Question1/text()) &gt; 0 ) )"
                 required="true()"
                 type="xf:date"/>
            <xf:bind id="Question4-bind" name="Question4" readonly="false" ref="Question4"
                 relevant="(( xs:date($Question2) &gt; xs:date('2015-05-20')) or ( $Question1='Answer2' ) )"
                 required="true()"/>
        </xf:bind> 
        </xf:bind>
        <xf:instance id="fr-form-attachments">
        <attachments>
            <css filename="" mediatype="text/css" size=""/>
            <pdf filename="" mediatype="application/pdf" size=""/>
        </attachments>
        </xf:instance>
        <xf:instance id="fr-form-resources" xxf:readonly="false">
        <resources>
            <resource xml:lang="en">
            <Category>
                <label>Category</label>
                <hint/>
                <alert/>
                <Question1>
                <label>Question1</label>
                <hint/>
                <alert/>
                <item>
                    <label>Answer</label>
                    <hint/>
                    <value>Answer</value>
                </item>
                <item>
                    <label>Answer2</label>
                    <hint/>
                    <value>Answer2</value>
                </item>
                </Question1>
                <Question2>
                <label>Question2</label>
                <hint/>
                <alert/>
                </Question2>
                <Question3>
                <label>Question3</label>
                <hint/>
                <alert/>
                </Question3>
                <Question4>
                <label>Question4</label>
                <hint/>
                <alert/>
                <item>
                    <label>Answer</label>
                    <hint/>
                    <value>Answer</value>
                </item>
                <item>
                    <label>Answer2</label>
                    <hint/>
                    <value>Answer2</value>
                </item>
                </Question4>
            </Category>
            </resource>
        </resources>
        </xf:instance>
        <xf:instance id="fr-service-response-instance" xxf:exclude-result-prefixes="#all">
        <response/>
        </xf:instance>
    </xf:model>
    </xh:head>
    <xh:body>
    <fr:view>
        <fr:body xmlns:xbl="http://www.w3.org/ns/xbl"
             xmlns:oxf="http://www.orbeon.com/oxf/processors"
             xmlns:p="http://www.orbeon.com/oxf/pipeline"
             xmlns:dataModel="java:org.orbeon.oxf.fb.DataModel">
        <fr:section bind="Category-bind" class="webforms-element webforms-category"
                id="Category-control">
            <xf:label mediatype="text/html" ref="$form-resources/Category/label"/>
            <xf:hint ref="$form-resources/Category/hint"/>
            <xf:alert ref="$form-resources/Category/alert"/>
            <xh:tr>
            <xh:td>
                <xf:select1 appearance="full" bind="Question1-bind"
                    class="webforms-element webforms-question"
                    id="Question1-control">
                <xf:label mediatype="text/html" ref="$form-resources/Category/Question1/label"/>
                <xf:hint ref="$form-resources/Category/Question1/hint"/>
                <xf:alert ref="$form-resources/Category/Question1/alert"/>
                <xf:itemset class="webforms-element webforms-answer"
                        ref="$form-resources/Category/Question1/item">
                    <xf:label ref="label"/>
                    <xf:value ref="value"/>
                    <xf:hint ref="hint"/>
                </xf:itemset>
                </xf:select1>
            </xh:td>
            </xh:tr>
            <xh:tr>
            <xh:td>
                <xf:input bind="Question2-bind" class="webforms-element webforms-question"
                      id="Question2-control">
                <xf:label mediatype="text/html" ref="$form-resources/Category/Question2/label"/>
                <xf:hint ref="$form-resources/Category/Question2/hint"/>
                <xf:alert ref="$form-resources/Category/Question2/alert"/>
                </xf:input>
            </xh:td>
            </xh:tr>
            <xh:tr>
            <xh:td>
                <xf:input bind="Question3-bind" class="webforms-element webforms-question"
                      id="Question3-control">
                <xf:label mediatype="text/html" ref="$form-resources/Category/Question3/label"/>
                <xf:hint ref="$form-resources/Category/Question3/hint"/>
                <xf:alert ref="$form-resources/Category/Question3/alert"/>
                </xf:input>
            </xh:td>
            </xh:tr>
            <xh:tr>
            <xh:td>
                <xf:select1 appearance="full" bind="Question4-bind"
                    class="webforms-element webforms-question"
                    id="Question4-control">
                <xf:label mediatype="text/html" ref="$form-resources/Category/Question4/label"/>
                <xf:hint ref="$form-resources/Category/Question4/hint"/>
                <xf:alert ref="$form-resources/Category/Question4/alert"/>
                <xf:itemset class="webforms-element webforms-answer"
                        ref="$form-resources/Category/Question4/item">
                    <xf:label ref="label"/>
                    <xf:value ref="value"/>
                    <xf:hint ref="hint"/>
                </xf:itemset>
                </xf:select1>
            </xh:td>
            </xh:tr>
        </fr:section>
        </fr:body>
    </fr:view>
    </xh:body>
</xh:html>

The interesting point is Question4, with the relevant rule (( xs:date($Question2) > xs:date('2015-05-20')) or ($Question1='Answer2')).

If I write first the part of xs:date($Question2) > xs:date('2015-05-20') and later the part of $Question1='Answer2' in the OR operator, when Question1 has selected the Answer2, Question4 is not shown. That means that the right part of the OR operator is ignored. But if I change the relevant rule as (( $Question1='Answer2') or (xs:date($Question2) &gt; xs:date('2015-05-20'))) the behaviour works perfect in both cases: Question4 is shown if Question1 = answer or the date field has a correct value.

Seems that having the date at the first place is giving me some problems. Probably because if Question3 is not fill up due to the flow, date has an empty value and the relevant rule is not run correctly.

I have tried also some alternatives as checking the length of the field adding string-length(format-date($Question3), '[MNn,*-3]/[D01]/[Y]')) > 0 or if(not(normalize-space($Question2) = '')) then ( xs:date($Question2) &gt; xs:date('2015-05-20')) else false both with the scope of ignoring the processing of the date field operator if it is empty. But no luck at all.

With other different types of date, the relevant rule works normally, and therefore I think that the issue is with my date field.

If this is the expected behaviour, the question is: How can I use the date field in a correct way to have a relevant rule working correctly?


Solution

  • First, you don't need to use xs:date($foo) when foo is a date, with type annotations enabled, which is now the default for forms you create in Form Builder. But this won't help much in your case, as when $Question2 > xs:date('2015-05-20') is evaluated, if $Question2 isn't a date, the whole expression will fail, just as if you had written xs:date($Question2). So in your case, you need to either:

    • Test on the checkbox first (you already know that this works).
    • Write $Question2 castable as xs:date and $Question2 > xs:date('2015-05-20'), so this part of the expression doesn't fail but returns false when $Question2 isn't a date.

    We've seen several variations of this problem, and we're currently thinking that we can solve this by changing the XForms engine to return an empty sequence when the typed value of $Question2 is accessed and $Question2 isn't a date (or whatever type it is supposed to be). This will be equivalent to () > xs:date('2015-05-20'), which returns false, and would work in your case. If you're interested, this is covered in more detail in RFE 2251. But again, for now, you'll have to resort to one of the two techniques described above.