Allowing typed data elements to be valid when empty

To indicate that some element is valid even if it is empty, we can use the XSD Schema attribute xsd:nillable in the schema definition. To illustrate, say we wanted to have some data that must be a decimal number between 0 and 5. But additionally, if the user doesn't provide a value, we don't want this to break validity. In XML Schema we would do the following (note how nillable is an attribute of xsd:element, not of the data type being created):

<xsd:element name="some-elem" type="myType" nillable="true" />

<xsd:simpleType name="myType">
  <xsd:restriction base="xsd:decimal">
    <xsd:minInclusive value="0" />
    <xsd:fractionDigits value="5" />
  </xsd:restriction>
</xsd:simpleType>

Although this might seem quite a bit of work, it is still more logical than the technique that is often used by newcomers to XForms and schemas, which is to create a type that is a union of some data type and the empty string. This technique not only requires us to keep adding new unions whenever we want an element that can be empty, but it is also unlikely to be a reflection of the underlying logic.

To illustrate of why a simple union is often incorrect, consider a ticket booking system. If I book a single--or one-way--ticket, there is no return date for my trip--it's 'empty' or nil. This is very different to saying that my return date is 'a date of zero length', which is what is implied by the approach that creates a new type which allows the empty string.

Using xsi:nil

Once an element has been set up with @xsd:nillable in the schema, then if it is ever empty, it won't break schema validation provided that it has the attribute xsi:nil set to a value of true:

<some-elem xsi:nil="true" />

This is extremely useful from an XForms point of view, since we can manipulate this attribute in our instance data just like any other data. We could, for example, link it to a 'checkbox' to indicate whether the element is nil or not. So, in a form asking a user for their travel dates, we could use xsi:nil to indicate whether the user wants a return ticket, and then hide the return date control if they don't (see Example 1). Conversely, we could also use a calculation to automatically set the element to nil (by settting the attribute to true) if the element is empty (see Example 2).

Examples

Example 1

This example asks the user for two dates for a journey--the outward and return parts of the trip. If the customer only wants a single, or one-way ticket we set the value of the return date to nil.

First we set up the model with instance data:

<xf:model>
  <xf:instance id="my-order">
    <order xmlns="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <travel-date>2005-05-07</travel-date>
      <return-date xsi:nil="false">2005-05-07</return-date>
    </order>
  </xf:instance>

Next we indicate the data type of the two dates, and set the return date to only be displayed to the user if the xsi:nil attribute is true:

  <xf:bind nodeset="travel-date" type="xsd:date" />

  <xf:bind nodeset="return-date" type="xsd:date" relevant="boolean-from-string(@xsi:nil)=true()">
    <xf:bind nodeset="@xsi:nil" type="xsd:boolean" />
  </xf:bind>
</xf:model>

The user interface for this model is quite straightforward. All we need are normal input controls for each of the three pieces of information we need to collect, and XForms does the rest for us. If the user indicates that they want a return trip (i.e., sets the xsi:nil attribute to true via the check-box) then the second date input is shown:

<xf:input ref="travel-date">
  <xf:label>Travel Date:</xf:label>
</xf:input>

<xf:input ref="return-date/@xsi:nil">
  <xf:label>Return trip?</xf:label>
</xf:input>

<xf:input ref="return-date">
  <xf:label>Return Date:</xf:label>
</xf:input>

Example 2
Whilst the previous example allows the user to set the nillable value explicitly, this example works the other way round--if the value of an element is '' (the empty string) then the element is set to nil, ensuring that it will pass schema validation.



xmlns=""
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>
xsi:type="xsd:nonNegativeInteger">0