XForms for HTML Authors, Part 2 (DRAFT)

Introduction

This is the second part of XForms for HTML Authors, introducing XForms 1.1.
Part 1 dealt mostly with features that have some
equivalent in HTML. This part introduces completely new concepts that have
little or no HTML equivalence.

Events and actions

XForms uses XML Events for
handling events: this is a more flexible way of doing the onclick
style of event handling used in HTML. The important thing to know about XML
Events is that it uses exactly the same event mechanism as HTML, only
written differently.

Consider this simple HTML example:

<button name="OK" onclick="alert("You clicked me!"); return true;">

This says that if the <button> element (or any of its
children) gets the click event, then the piece of code in the
onclick attribute is performed.

We say "or any of its children" because in a case like

<a href="..." onclick="...">A <em>very</em> nice place to go</a>

or

<a href="..." onclick="..."><strong>More</strong></a>

you want the onclick to be performed even if the click actually
happens on the <em> or <strong> elements.
In these cases we call the element that was clicked on the target, and
the element that responds to the event an observer (though target and
observer are often the same element).

So what you see is that there are three important things involved: an event,
an observer, and a piece of script (called a handler). As you can see
from the above, we usually aren't worried about which element was the
target.

There are some problems with the way HTML specifies the relationship between
the three:

the event name is hard-wired into the language, rather than being a
parameter, so that to be able to deal with a new sort of event you have to
add a new attribute, like onflash if an event called
flash were introduced.

the event name is usually very hardware specific, such as
click, when in fact you don't care how the button is
activated, only that is has been activated

you can only use one scripting language (since you can't have two
attributes called onclick, one for JavaScript and one for
VB)

event handling and markup are intertwined — there are no ways to
separate the two.

XML Events specifies the relationship between the event, observer and
handler in a different way. The equivalent to:

This says that the <message> element is a handler for the
DOMActivate event (which is used in preference to
click, because triggers can be activated in different ways, not
just by clicks), and in the absence of any other information, the parent
element is the observer (<trigger> in this case). Note that
elements that have ev: attributes are evaluated only when the
event happens, and not when the document is loading, as
<script> is in HTML.

Note that the event attribute has a prefix ev: for
the XML Events namespace; this means that you have to have a declaration for
the prefix somewhere suitable in your document:
xmlns:ev="http://www.w3.org/2001/xml-events".

You can catch more than one event by having more handlers for an element:

All of XForms processing, from the initial loading of the instance, to the
submission, is based on events; you can listen to almost every part of the
processing model; see the XForms Events
Overview for details. Most examples here use DOMActivate.

Switch

The switch element allows you to expose and hide different parts of the user
interface, for example to give wizard-like behavior. Here is an example that
first asks for name, city, and email address, and then asks for favorite food,
drink and music. (Try it; you don't need to enter anything, just click on
next)

In fact this is slightly too simple, since you shouldn't really toggle to
the start case until you know that the submission has been successful. To do
that you should submit, and then wait for the success event. We can fix this by
replacing the Submit trigger above with the following:

The instance in this case consists of a number of to-do items, where each
item consists of a task name, a status, and a date (these are not XForms
elements, but elements in the instance that we want to change using XForms
controls):

The current item

Each repeat has an index associated with it that indicates the
'current item' in that repeat. It is initially 1, but if you use a control in a
repeat, the index gets set to that row. You can also set it explicitly with a
<setindex/> action. You can make the current row visible by
styling it with the CSS ::repeat-index selector (see the later
section on styling):

If a repeat element has an id, then you can access its index
with the function index using the id value to
identify the repeat. So using that you can add elements to a repeating set
relative to the current element as well as at the beginning and end. If we add
an id to the repeat above:

<repeat nodeset="todo" id="todo-repeat">

we can then insert a new item after (position="after") the
current position (at="index('to-do-repeat')") with:

Initializing inserted items

Since any new item is initialized from the last item in the initial data,
you will probably want to copy values into any new element inserted in a list.
So instead of just an insert, we bundle all the actions in an
<action> element, and we set each value with a
setvalue action:

The first setvalue just sets the status of the
inserted item to the string "unstarted"; the second sets its task
to the empty string, and the third calculates today's date. The function
now() returns a date and time string like
2010-11-26T09:19:33+1:00 (this is a standard format described by
ISO) which consists of the date, the letter T, the local time (on your
computer) and then the offset of the local time from the universal timezone UTC
(+ or - some number of hours and minutes, or Z if you are in the
UTC time zone). So the substring-before expression just returns
all the text before the letter T, i.e. today's date.

Deleting items in a repeat

To delete an item, you can use this, for example next to the 'new'
button:

However, the best place to include it is in the repeat, so you get one
delete button per item (as in the example above), making it obvious which item
is being deleted (since the repeat current item gets set to the row the button
is in when you press it). Since within the repeat the context has already been
set to the item set, you use nodeset=".":

Inserting into an empty list

Above we inserted a new todo item into the list with the action

<insert nodeset="todo" position="after" at="count(todo)"/>

What this does is takes the last item from the existing list, and copies it
to the position given. However, this doesn't work when the list is empty (for
example, if you delete all existing items), and this is for two reasons: there
is no list to insert into any more, and there is no item to make a copy of. For
this reason, if the list can ever be empty you have to extend the insert action
with two pieces of information, the context, and a template of what to
copy. First we make an extra instance containing a template.

but later discover you want to add another language not only to this form
but also to several others that offer the same choice. Better then to put the
choices in a single file, and then load that into an instance, and refer to
that instead:

Another option is to have all the messages for all the languages in one
place. The format could be any one of a number, for example first one message
in each language, then the next message in each language, and so on:

This has two downsides: firstly it is very un-XML like, since it is
essentially packing several values into a single string, forcing you to unpack
it later. Secondly it means that you can't include values that contain a space:
you can't select from a list of cities like New York, San Francisco, Las
Vegas.

The reason that the basic form is like this is because it is what HTML Forms
do, and XForms needs to be able to talk to servers that expect data in the HTML
Forms style.

However, XForms also allows you to select on structured data, which does
allow you to have spaces in the data.

For example, suppose we want to select from a set of cities, and return data
like:

and then refer to that with the following select. Note that we
now use itemset instead of item, since we are getting
the values from an instance, and we use copy instead of
value because we are copying a whole structure (such as
<city>New York</city>, and not just a value such as
"New York":

Help, Hint and Alert

All controls with the exception of <output> can also have
<help>, <hint> and
<alert> elements as well as a <label>
element. These are to give extra information to the user for different
purposes:

help: information when the user asks for help, for example when pressing
the help key;

hint: ephemeral information, such as hover text, that gives a hint on how
to fill a value in;

alert: information about a value when it is invalid.

<input ref="code">
<label>Security code</label>
<hint>The 3 or 4 digit number on the back or front of your card</hint>
<help>This is a three or four digit security code that is usually either
on the front of your card just above and to the right of your
credit card number, or the last three digits of the number printed
on the signature space on the back of the card.</help>
<alert>Must be three or four digits.</alert>
</input>

Defining your own types

This is not the place for a tutorial on XML Schema, but suffice to say that
if you already have a schema defining some data types, then all you have to do
to use it in your XForms is to refer to it from the model element:

<model schema="http://www.example.com/schemas/types.xsd">
...

or you can include a schema directly in the body of the model element:

Of all the facilities that XML Schema provides for defining data types,
XForms has equivalents for them all with one exception: patterns. These define,
using regular expressions, the pattern that a value must conform to. For
example, here is a new simpleType called curse, that
is a restriction on the base type string:

Apart from documenting the use of privacy-related values, this also gives
the opportunity for user agents to prefill such values for you, and to warn you
about their use.

Styling

XForms elements are styled using CSS. The only thing that you have to watch
out for is that different implementations use different levels of CSS, so that
sometimes you have to repeat some styling rules.

The main problem is with CSS1 and 2, which don't know about namespaces, so
that if you have written your XForms elements using prefixes such as

<xf:label>Age:</xf:label>

then you have to write your CSS selectors including the same namespace
prefix:

xf\:label {background-color: yellow}

However, a CSS3 and higher stylesheet doesn't need the prefix, and so
(assuming there are no <label> elements from other
namespaces in your document), you can just write:

label {background-color: yellow}

There are some special selectors in CSS3 for addressing particular dynamic
cases of XForms use, and many implementations already support them. In
particular, you will see:

:valid and :invalid -- these match the
validity of data types, and 'constraint' bindings.

*:invalid { background-color: red}

Note that in the invalid case, the <alert> element
becomes activated as well, probably by using rules like:

:in-range and :out-of-range -- these
match range controls where the instance value does not fall
within the bounds, and select1 and select
controls where the instance value does not match any of the items.

:required and :optional -- these match
the 'required' binding

*:required {border: thin red solid}

:read-only and :read-write -- these
match the 'readonly' binding

*:read-only {color: gray}

:enabled and :disabled -- these match
the 'relevant' binding

*:disabled {display: none}

::value -- this selector matches the part of a control
where the value is displayed/typed in

input::value {width: 10em}

::repeat-item -- this matches each row of a repeat

::repeat-index -- this matches the currently active row
of a repeat.

::repeat-index {background-color: #ccf}

Implementations that only support CSS1 or 2 often implement these as special
values of the 'class' attribute. For example:

input.invalid {border: thin red solid}

and you should check in your implementation documentation which get used.
However, implementors have recently agreed to coordinate these values, so that
they will all use the same names. The agreement is to start class names
representing pseudo-classes with "-pc-" and then the name of the class, and for
pseudo-elements "-pe-" and then the element name. For example:

Some techniques

Page counts

The traditional technique to display how many people have visited a page is
to keep a count of the number of hits, and when the page is requested to
generate an image for that number, which then gets displayed in the page. An
image of several thousands of bytes is of course a rather inefficient way of
transferring about half a dozen bytes of information .

With XForms it is much simpler. You just keep a file with a count of the
number of hits:

<n>56356</n>

import it into an instance:

<instance src="hits.xml"/>

and display the number of hits with an <output>:

<output ref="/n"><label>Number of hits:</label></output>

Initializing instance values

When you use a bind for a value, like:

<bind nodeset="today" calculate="substring-before(now(), 'T')"/>

then the value 'today' will be set invariantly to that value: you can never
change it. However, if you just want to initialize a value when the form gets
loaded, and let the user change it, then you can use a setvalue
action, listening for the event xforms-ready, which gets sent to
the model element:

Trigger styling

Although not defined as such in the XForms specification, an emerging
practice amongst implementations is to style <trigger
appearance="minimal"> as text instead of as a button. So for example
instead of using the show/edit style of <switch> above, you
can make each individual field switchable:

Model-based switching

Although using <switch> is useful for exposing and hiding
parts of the interface, sometimes it is useful to be able to affect the display
on the basis of values in the instance. With CSS-based implementations, you can
do this using a technique called 'model-based switching'.

The essence of the idea is to bind a <group> to a value
that can be made relevant or not, and to hide irrelevant groups:

<group ref="...">
...
</group>

and in the CSS (although this is usually the default anyway):

group:disabled {display: none}

So for example, we don't want to ask questions about someone's husband or
wife if they are not married:

Using this technique, you can use a trigger to change a value that causes
some controls to become available. Here, the value called toggle
is used to control which cases are visible. It is initially 1, and so initially
the value case[1] is relevant. A trigger causes
toggle to be set to 2, making the next case relevant:

Master/detail views

There are times when you don't want to look at all the items in a repeating
structure, but want to make a selection, or you want to see a summary, and then
select items for detailed inspection. This is when you can use master/detail
forms. There are several ways to achieve this. Let us take the to-do list
example from earlier, and treat it in a different way. In the example above we
displayed all the elements from the list. Now we are going to display just
one.

You'll notice that if you click through past the beginning or end of the
list that nothing more gets displayed (including the buttons). This is because
of the way that todo[instance('admin')/index] works: if
index goes below 1 or above the number of items in the to do list,
nothing is selected. (If the triggers had been outside the group, they would
have remained visible.)

What we need to do is disable the triggers when they reach the end items, so
you can't go any further. To do this we add two more elements to the
admin instance, along with a couple of binds:

We don't care about the value of the two new elements (they are both empty
strings), but we do care when they are relevant. The element
notfirst is relevant when index is greater than one,
and the element notlast is relevant when index is
less than the number of items in the to do list. The reason we want these is to
bind the triggers to them:

The select1 is exactly the same, but we will add triggers
before and after it, one to get the previous item and one to get the next item.
For the previous item what we want to do is set the selected string in the
admin instance to the task of the previous item:

Now what goes in "...something here..."? We know how to find the
to-do item that has the selected task:

todo[task=instance('admin')/selected]

To find the previous to-do item we use preceding-sibling, which
returns a list of all the to-do items before the one selected:

todo[task=instance('admin')/selected]/preceding-sibling::todo

and then take the first of these (i.e. the first preceding sibling)

todo[task=instance('admin')/selected]/preceding-sibling::todo[1]

and finally we select the task field of that item:

todo[task=instance('admin')/selected]/preceding-sibling::todo[1]/task

The trigger to find the next item is exactly the same, except you use
following-sibling instead of preceding-sibling.

Finally, what should we bind to notfirst and
notlast in order to make the triggers irrelevant when they are at
the first and last item? Well, the 'previous' trigger is only relevant when
there are previous items available:

Dynamic URLs

One of the new features of XForms 1.1 is the ability to use
<output> to display an image. For instance, the excellent Open Streetmap project is producing a
street map of the world that anyone can edit, like Wikipedia. Their street map
is represented by images called tiles that can be tiled together, and
cover the whole world. For instance a tile for the centre of London is at
"http://a.tile.openstreetmap.org/10/511/340.png":

If we investigate the URL above, we find that "10" represents the zoom
level, and "511" and "340" represent horizontal and vertical dimensions, so
that the next tile east from the above one is
"http://a.tile.openstreetmap.org/10/512/340.png" and the one
north would be
"http://a.tile.openstreetmap.org/10/511/339.png". So let's
express that:

Now, if we further investigate the structure of the Open Streetmap URLs, we
find that the world is split into tiles, so that at zoom level 0 there is one
tile, zoom level 1, 4 tiles, zoom level 2, 16 tiles, and so on. Furthermore
tile 0,0 is the most northern, most western. What this means is that as you
zoom out, you have to halve the values of x and y. So we will keep our current
position as the x and y coordinates at the innermost zoom level, and calculate
the current tile based on that: