Creating Applications: Adding Multiple-Choice Fields and Values

Up to this point, we have only considered two kinds of Web form fields: text entry fields and action buttons. Since most Web forms offer more convenient ways of entering certain kinds of data, we shall now investigate multiple-choice fields as an example of how XSLForms handles more complicated types of fields.

We shall revise our form data structure to be the following:

<?xml version="1.0"?>
<structure>
<item value="some value">
<type value="some type"/>
<subitem subvalue="some other value"/>
</item>
</structure>

Single-Valued Fields

Whilst HTML offers types of form fields where users can select one or many values presented in a list or menu, we shall first consider the case where only a single value can be chosen from such a selection.

Some item: 

Item type:

Itself containing more items:

Sub-item:

From the item type list only one value may be selected.

Taking the example HTML code from before, we can add a definition of this new list to the template to produce something like this:

<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:template="http://www.boddie.org.uk/ns/xmltools/template">
<head>
<title>Example</title>
</head>
<body template:element="structure">
<form action="" method="POST">

<!-- Template text between the start and the interesting part. -->

<div template:element="item">
<p>
Some item: <input template:attribute="value" name="{template:this-attribute()}" type="text" value="{$this-value}" />
<input name="remove={template:this-element()}" type="submit" value="Remove" />
</p>
<p>
Item type:
<select template:element="type" name="{template:new-attribute('value')}">
<option template:element="type-enum" template:expr="@value = ../@value" template:expr-attr="selected"
template:value="@value" value="{@value}" />
</select>
</p>
<p>
Itself containing more items:
</p>
<p template:element="subitem">
Sub-item: <input template:attribute="subvalue" name="{template:this-attribute()}" type="text" value="{$this-value}" />
<input name="remove2={template:this-element()}" type="submit" value="Remove" />
</p>
<p>
<input name="add2={template:this-element()}" type="submit" value="Add subitem" />
</p>
</div>
<p>
<input name="add={template:this-element()}" type="submit" value="Add item" />
</p>

<!-- Template text between the interesting part and the end. -->

</form>
</body>
</html>

There are a lot of details here that need to be explained. Here is what was done:

  1. A paragraph was added to provide some space on the page for the field.
  2. Inside the paragraph, next to the label text, an HTML select element was added.
  3. The select element is mapped onto the type element in the form data structure. However, HTML fields must produce values and it makes no sense to interpret a textual value as an element. Therefore, we indicate in the name of the select element that the value submitted maps onto the value attribute of the type element in the form data structure.
  4. Inside the select element, we include an option element which defines the values which will be presented to users of the form. Note that the option element maps onto a type-enum element which is not mentioned in our revised form data structure above; this will be discussed below.

Output Structures

Although we revised the form data structure above, and whilst the revised structure can describe form data submitted by users of our application, it is unfortunately not sufficient to define the form data that is to be presented. Consider the multiple-choice values that shall be presented to users: such values are not defined in our revised structure. Therefore, we shall define an output form data structure as follows:

<?xml version="1.0"?>
<structure>
<item value="some value">
<type value="some type">
<type-enum value="choice #n"/>
</type>
<subitem subvalue="some other value"/>
</item>
</structure>

Presenting the Extra Values

In the template, the option element is presented using a number of special annotations which make more sense when considering the above output structure:

The result of this is that the type element in the this example structure fragment...

<type value="2">
<type-enum value="1"/>
<type-enum value="2"/>
<type-enum value="3"/>
</type>

...is transformed into something resembling this HTML code:

<select name="...">
<option value="1">1</option>
<option value="2" selected="selected">2</option>
<option value="3">3</option>
</select>

Such presentation techniques are sufficient if the input form data structure is identical to the output structure, but since we will receive a structure resembling that defined earlier (where the multiple-choice values are never sent back to the application), yet need to present a structure like the one above, we will need to find a way of merging the range of allowed values into the user-edited form data before presenting that data using our template.

Merging Values into the Form Data

There are many possible ways of inserting extra XML elements into an existing XML document, but we shall concentrate on using an XSL stylesheet to perform this merge operation. First, let us define a document containing all the possible values for the type field:

<?xml version="1.0"?>
<types>
<type-enum value="(Not selected)"/>
<type-enum value="Important"/>
<type-enum value="Not important"/>
<type-enum value="Personal"/>
</types>

We shall refer to this document when inserting the different type-enum elements into our input form data structure to produce the output structure described above. The stylesheet which performs this task is quite scary for those not familiar with XSL, but it works on the following principles:

  1. Descend into the form data structure, copying all elements, attributes and text that the stylesheet is not programmed to recognise.
  2. When encountering an item element (which the stylesheet is programmed to recognise), do the following:
    1. Copy the element "skeleton" and its attributes so that the value attribute is retained.
    2. Look for a type element inside the item element.
    3. Where a type element exists, process it; otherwise, make a new type element and process that.
  3. When encountering an existing type element, do the following:
    1. Copy the element "skeleton" and its attributes so that the value attribute is retained.
    2. Add the type-enum elements from the document defined above.
  4. When processing a new type element, do the following:
    1. Add the type-enum elements from the document defined above.

The stylesheet source code can be found in examples/Common/VerySimple/Resources/structure_types.xsl, whereas the document defined above which contains the values can be found in examples/Common/VerySimple/Resources/structure_types.xml.

Performing the Merge

To take advantage of these new documents, it is necessary to introduce some code into the Web resource to perform the merge operation. The special WebStack resource that we subclassed earlier provides some convenient mechanisms for introducing XSL-based transformations, and we shall add a few extra attributes to our resource class in order to take advantage of them:

    # Under template_resources...

transform_resources = {
"types" : ["structure_types.xsl"]
}
document_resources = {
"types" : "structure_types.xml"
}

These attributes define the following things:

  1. A transformation called types which uses the structure_types.xsl stylesheet file.
  2. A document referred to by the name types which is provided by the structure_types.xml file.

To actually perform the merge operation, we need to add a few extra lines of code after the addition and deletion operations in the respond_to_form method:

        # Under the addition and deletion operations...

# Transform, adding enumerations/ranges.

types_xsl_list = self.prepare_transform("types")
types_xml = self.prepare_document("types")
structure = self.get_result(types_xsl_list, structure, references={"types" : types_xml})

# Start the response.

These lines do the following things:

  1. Obtain the stylesheets for the types transformation.
  2. Obtain the types document containing the values to be merged into the form data.
  3. Take the stylesheets and apply them to the form data, structure, using a reference to the types document containing the values.

The result of this should be a processed structure document containing the type values for each type element in that document.

Other Multiple-Choice Data

We have now added a simple, single-valued multiple-choice field to the application. However, many applications often need to obtain multivalued multiple-choice data, and this kind of information is investigated in the next part of the development process.