One fashionable avenue in Web application design has been that of updating Web pages in applications without having to refresh the entire page every time an action is performed. Together with some JavaScript support in the browser, XSLForms also provides some functionality for such "in-page" or "live" updates.
Consider the addition of a comment field to our application. Here is how the HTML code might look:
<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:list-attribute('type-enum', 'value')}" multiple="multiple">
<option template:element="type-enum" template:expr="@value-is-set" template:expr-attr="selected"
template:value="@value" value="{@value}" />
</select>
</p>
<p template:element="options">
<span template:element="comment">Comment:
<textarea template:attribute="value" name="{template:this-attribute()}" cols="40" rows="3">
<span template:value="$this-value" template:effect="replace">Some comment</span>
</textarea>
</span>
</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>
The newly-added textarea
field will not be
presented in the application in its current state; this is due to the
lack of any options
or comment
elements
manipulated by the
application, and such template changes are actually quite safe to make.
So, we must now do some additional work to add such options
and comment
elements in our application.
One approach is to extend our transformation which adds the different type values so that these new elements are introduced as well. In the Web resource, we can make the following change:
transform_resources = {
"types" : ["structure_multivalue_types.xsl", "structure_comments.xsl"]
}
What this does is to state that when we carry out the types
transformation, two stylesheets are employed, one before the other,
such that the type values are first added using the first stylesheet
(and the additional reference document containing the type values) and
that the comments are then added using the second stylesheet.
This new stylesheet works according to the following principles:
item
element (which the
stylesheet is programmed to recognise), do the following:value
attribute is retained.options
element and process it.options
element, do the
following:options
element, investigate
the values associated with the type
element.comment
element, then add any attributes that may be found on existing
comment
elements within the current type
element.Since this stylesheet is used after the type value transformation,
we may (and even must) take advantage of the results of that
transformation, including noting that selected values on type-enum
elements are marked with the value-is-set
attribute.
The stylesheet source code can be found in examples/Common/VerySimple/Resources/structure_comments.xsl
.
Whilst the above modifications adds a comment field for each item
with a type of "Personal", and whilst the comment field will appear and
disappear for items as their type changes, such updates only take place
when items and subitems are added and removed. We could add an update
button to the page which performs an explicit refresh of the page
without adding or removing anything, and for the sake of usability, we
probably should add such a button (just below the Add item
button):
<p>
<input name="update" type="submit" value="Update" />
</p>
However, we could also add an in-page update to make each comments field appear and disappear as soon as we have changed the type of an item.
We must first define a region of the template where a comment fields can be added and removed, regardless of whether such a field existed there before. The above template code needs modifying slightly to permit this:
<p template:element="options" template:id="comment-node" id="{template:this-element()}">
<span template:element="comment">Comment:
<textarea template:attribute="value" name="{template:this-attribute()}" cols="40" rows="3">
<span template:value="$this-value" template:effect="replace">Some comment</span>
</textarea>
</span>
</p>
Here, we have added this region definition to the paragraph surrounding the comment field, annotating the paragraph with the following attributes:
template:id
attribute is used to define a
template fragment used only to prepare the updated part of the Web
page. Here we define the fragment or region as being just this
paragraph.id
attribute is used to
define which part of the active Web page will be replaced when
performing an in-page update. This attribute needs to have a unique
value, but the easiest basis for such a value is a selector-style
reference to the options
element within which the comment
element resides.Another change has been to put the template:element
annotation inside the above fragment or region annotations. Had we not
done this, the lack of a comment
element in the form
data could have prevented the id
attribute from
appearing in the Web page, this preventing any hope of an in-page
update since there would be no way of knowing where such an update
should be applied.
Since we rely on JavaScript support in the browser, the following references to scripts must also be added to the template, as shown in the following excerpt:
<head>
<title>Example</title>
<script type="text/javascript" src="scripts/sarissa.js"> </script>
<script type="text/javascript" src="scripts/XSLForms.js"> </script>
</head>
These special script files can be found in examples/Common/VerySimple/Resources/scripts
.
Now we can concentrate on adding the event which triggers an in-page update. Since it is the type values that cause each comment field to be added or removed, we add an event attribute on the form field responsible for displaying the type values:
<p>
Item type:
<select template:element="type" name="{template:list-attribute('type-enum', 'value')}" multiple="multiple"
onchange="requestUpdate('{$application-url}comments', '{template:list-attribute('type-enum', 'value')}',
'{template:other-elements(../options)}', '{template:other-attributes('value', ../options/comment)}',
'/structure/item/options')">
<option template:element="type-enum" template:expr="@value-is-set" template:expr-attr="selected"
template:value="@value" value="{@value}" />
</select>
</p>
This complicated string calls a special update request JavaScript function which triggers the in-page update, and it specifies the following things:
application-url
which will need to be provided to the template when generating the Web
page.type
element and its type-enum
elements' value
attributes, we specify the names of
the fields which yield these values.options
element holding comment
element. Thus, we use a
special value which also refers to that element from the context of
the type
element.comment
element from the type
element and stating that we want the
value
attribute on that comment
element.