Tutorial: Declarative Web Applications with XForms

HAVE YOU INSTALLED THE SOFTWARE?

cwi.nl/~steven/xforms/xforms-hands-on/

Steven Pemberton, CWI, Amsterdam

Contents

Introduction

Install the server!

Solutions and a troubleshooting guide at the back.

Quick reference guide

The tutorial is written in XForms

But first a very quick introduction to XForms

The structure of an XForm

An XForm is an XHTML document with an extra element in the <head> element, called <model> that contains 'instances' of data and details of that data, such as types and relationships, and then a group of 'controls' in the body of the XHTML that use that data:

<?xml-stylesheet href="../xsltforms/xsltforms.xsl" type="text/xsl"?>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://www.w3.org/1999/xhtml"
      xmlns:ev="http://www.w3.org/2001/xml-events"
      xmlns:xf="http://www.w3.org/2002/xforms">
<head>
   <title>Your title here</title>
   <style type="text/css">
      CSS is used for styling
   </style>
   <model xmlns="http://www.w3.org/2002/xforms">
      <instance>...</instance>
      <instance>...</instance>
      other stuff
   </model>
</head>
<body>
   <group xmlns="http://www.w3.org/2002/xforms">
      Controls go here.
   </group>
</body>
</html>

The file has to be served up as an XML type: the easiest way is to use the file extension ".xhtml".

Data Instances and types

Within the model element in the head, you have one or more instances of data:

<instance>
   <data xmlns="">
      <product></product>
      <amount></amount>
      <due></due>
      <delivered></delivered>
   </data>
</instance>

Types can be assigned to the elements using bind elements, just after the </instance>:

<bind ref="amount" type="integer"/>
<bind ref="due" type="date"/>

The default type if you don't specify one is "string".

The Input Control

Input controls, which you put in the body of an XForm, bind to an element in an instance. For example:

<input ref="product"><label>product</label></input>
<input ref="amount"><label>amount</label></input>
<input ref="due"><label>due</label></input>

The controls adapt to the type.

By default an input updates the instance when you tab out of the control or otherwise leave it, but it will update at each keystroke if you add an attribute incremental="true":

<input ref="product" incremental="true"><label>product</label></input>

Exercise

The 'delivered' element in the instance hasn't been given a type, nor an input control attached.

Make it type 'boolean', and add an input control for it.

The output control

The output control uses a ref attribute in the same way to access elements and attributes in an instance; for full expressions you use a value attribute:

<output ref="price"/>
<output ref="product/@amount"/>
<output value="product/@amount * price"/>

In both cases there is an optional label:

<output value="product/@amount * price"><label>Total price</label></output>

Both ref and value are XPath expressions, which are like URLs in many ways: use "/" to go down, use ".." to go up. "@" for attributes, and "." for 'the current thing'.

So to access the amount attribute you use ref="product/@amount".

Exercise

Move the 'delivered' element so that it is an attribute of the 'due' element

Hint: there are 4 places you have to change:

  1. the instance,
  2. the bind,
  3. the input, and
  4. the output.

External Instances

In the previous examples, the instance contained its values inline, but you can obtain values from files, or over the internet:

<instance resource="currencies.xml"/>
<instance resource="http://www.floatrates.com/daily/eur.xml"/>

Formatting outputs

By default, outputs directly after each other will abut.

You can add characters between the outputs:

<output ref="a"/>, <output ref="b"/>

or combine them using value="..." and the concat(...) function:

<output value="concat('(', a, ', ', b, ')')"/>

or use CSS:

<output class="line" ref="a"/>

Exercise

Output what the publication date is, and how many <item> elements there are.

The repeat control

If there is a repeated element in an instance, the repeat control treats each in turn:

<repeat ref="country">
   <output ref="name"/>: <output ref="capital"/>
</repeat>

Context

Within the repeat you use the context of the containing expression; you say:

<output ref="name"/>

and not

<output ref="country/name"/>

This is true throughout, whereever controls are nested.

Exercise

In the currencies XForm, for each currency (each <item> element), output the value of the <title> element.

Multiple instances

You can have more than one instance. To identify which is which you give them id attributes:

<instance id="currencies" resource="currencies.xml"/>
<instance id="search">
   <search xmlns="">
      <q/>
   </search>
</instance>

To say which instance you are referring to, you use the instance function with the id:

<input ref="instance('search')/q"><label>Search</label></input>

You don't have to do this for the textually first instance, which is always the default instance.

Filtering nodes

You can select a subset of nodes by filtering:

<repeat ref="nodeset[condition]">...

For example, since the function contains(a, b) reports if a contains b anywhere in its content, then

<repeat ref="item[contains(., instance('search')/q)]">

repeats only over those item elements whose content contains the value in the q element in the search instance.

Exercise

In the currencies XForm add a search field, and only display those items that match the search field.

Hint 1: You can copy and paste a lot of what you need from the page.

Hint 2: Use incremental="true" on the input control.

Selections

Sometimes you want to offer the user a choice of values to input, rather than having to type them in.

There are two controls, select, which allows you to select a set of values, and select1 for a single value. We will just treat select1 for now.

<select1 ref="result">
   <label>Currency</label>
   <item>
      <label>Euro</label>
      <value>EUR</value>
   </item>
   <item>
      <label>Great Britain Pound</label>
      <value>GBP</value>
   </item>
   ...etc...
</select1>

This can be displayed in three different ways, using

<select1 ref="result" appearance="full">

or appearance="compact", appearance="minimal".

Exercise

In the currencies XForm where you added a search field, now add a select1.

The ref of the select1 (the result in other words) should be the element you used for the search field.

Dynamic Selections

It is possible to get the items for the select1 from an instance. For example with this instance:

<instance id="currencies">
   <valutas xmlns="">
      <valuta name="Euro">EUR</valuta>
      <valuta name="Great Britain Pound">GBP</valuta>
      ...etc...
   </valutas>
</instance>

you can create a select1 like this:

<select1 ref="result">
   <label>Currency</label>
   <itemset ref="instance('currencies')/valuta">
      <label ref="@name"/>
      <value ref="."/>
   </itemset>
</select1>

Watch out for the context of the itemset!

Exercise

In the currencies XForm where you added a select1, make the select1 dynamic, where the items are loaded from the currencies instance.

The labels should come from the <targetName> elements, and the values from the <targetCurrency> elements.

The ref of the select1 (the result in other words) should still be the element you used for the search field.

Hint: Don't forget to use instance('currencies') on the <itemset>.

Events and actions

Most things happen automatically; however events are dispatched that you can listen for and react to.

One of the most useful is xforms-ready, that announces that the XForm has started up and is initialised. You can listen for this in order to initialise values.

To listen for an event, you use an action element with the name of the event you want to react to:

<action ev:event="xforms-ready"/>
   ...
</action>

An action element should be placed within the element that the event is dispatched to. In the case of xforms-ready, that is the model element.

Within the action element you can put elements that cause things to happen (also called actions). A useful one is setvalue, that sets a value in an instance. For example

<action ev:event="xforms-ready"/>
   <setvalue ref="time" value="local-dateTime()"/>
</action>

Exercise

Change the given XForm so that it only displays the time, and not the date and time.

Hint: The function substring(s, start, length) returns the string s from position start, of the given length in characters.

Note: Because the event attribute uses a namespace prefix, that has to be declared somewhere, usually on the <html> element at the top: xmlns:ev="http://www.w3.org/2001/xml-events"

Triggers

A trigger is a control which by default looks like a button. Whenever the user clicks on it, a DOMActivate event is dispatched to it. For example:

<trigger>
   <label>Click me</label>
   <action ev:event="DOMActivate">
      <setvalue ref="count" value=". + 1"/>
   </action>
</trigger>

appearance="minimal" makes a trigger just text, and not a button.

Exercise

Add a trigger to the previous exercise that updates the time whenever it is clicked on.

Submission

<instance id="currencies" resource="http://www.floatrates.com/daily/eur.xml"/>

At initialisation, XForms goes out over the internet, gets the resource, parses the XML and puts the result into the instance.

This is a just a tiny bit of submission.

Submission allows you to send data from an instance to a server, collect the result, and store it somewhere. It has three essential parts:

  1. what you want to send,
  2. where you want to send it, and
  3. what you want to do with the result.

A <submission> element, which is put in the <model> element, specifies these things, but only actually does them when you tell it to.

Attributes

It has a lot of available attributes to cover a lot of options. Principally:

Examples

So here is a submission that describes the same as the instance above:

<submission
   resource="http://www.floatrates.com/daily/eur.xml"
   serialization="none" method="get"
   replace="instance" instance="currencies"/>

As another example, this replaces the whole page with the resource, the equivalent of the <load> action in the previous section:

<submission
   resource="events-exercise.xhtml"
   serialization="none" method="get"
   replace="all"/>

You can give a submission a name with an id attribute:

<submission id="geteur" ...

Initiating a submission

The easiest way is a submit control:

<submit submission="geteur">
   <label>Submit</label>
</submit>

which will initiate the submission called 'geteur'. This is actually just a shorthand for

<trigger>
   <label>Submit</label>
   <action ev:event="DOMActivate">
      <send submission="geteur"/>
   </action>
</trigger>

Exercise

Take the currencies XForm, and add two submit controls, one that loads http://www.floatrates.com/daily/eur.xml into the currencies instance, and the other that loads http://www.floatrates.com/daily/usd.xml (if you don't have an internet connection, use the file usd.xml instead).

Hint: you need two <submission> elements.

Submit error

After a submission is complete, an event is sent to the <submission> element, either xforms-submit-done, which says that the submission was successful, and which you can usually ignore, or xforms-submit-error, which is a good idea to listen for:

<submission resource="whatever" ...>
   <action ev:event="xforms-submit-error">
      ...
   </action>
</submission>

Event Properties

Some events when they happen have extra information available, which you can access with the event(...) function. For example, for xforms-submit-error, amongst others has:

You can do various things with this information, such as display it with a <message> action:

<submission resource="..." ...>
   <action ev:event="xforms-submit-error">
      <message>Submission error.
      error-type: <output value="event('error-type')"/>
      error-message: <output value="event('error-message')"/>
      etc
      </message>
   </action>
</submission>

or store the values in an error instance:

<submission resource="whatever" ...>
   <action ev:event="xforms-submit-error">
      <setvalue ref="instance('errors')/type"
         value="event('error-type')"/>
      ... etc ...
   </action>
</submission>

Exercise

Take the currencies XForm, now with two submits, and add actions to both submission elements to catch xforms-submit-error.

Try it out by changing one of the submissions so that the resource attribute has a non-existent or rubbish URL, or no resource attribute at all. What happens?

Calculate

It is possible to specify that a value in an instance depends on other values; whenever any of those values change, the dependent value gets updated.

 <bind ref="subtotal" calculate="../amount * ../price"/>
 <bind ref="tax" calculate="../subtotal * (../taxrate div 100)"/>
 <bind ref="shipping"
       calculate="if(../subtotal > 0 and ../subtotal &lt; ../shippingrate/@below,
                       ../shippingrate, 0)"/>
 <bind ref="total" calculate="round((../subtotal + ../tax + ../shipping)*100) div 100"/>

The ".."s are needed because of the context.

Exercise

Go back to the currencies XForm with the two submissions and a select1 at the top.

Hint 1: use concat(...).

Hint 2: Since calculations are in attributes with " double quotes, enclose strings in calculations with ' single quotes.

Hint 3: The currency code is in capitals, but the URL in lowercase. Use the lower-case(s) function to return the lowercase version of the currency code.

Hint 4: Don't forget to use instance('search') in the bind element.

Attribute value templates

To inject a value into an element, you use the <output> control. For example, the previous section used an output in a label:

<label>Tax at <output ref="../taxrate"/>%</label>

However, sometimes you want to inject a value into an attribute.

For this there are Attribute Value Templates (AVTs).

Suppose you want to output a balance with a red background if it is negative, and in green otherwise. So you define two CSS rules for negative and positive values, and then on the output, add an AVT to calculate which class to use:

<output class="{if(balance &lt; 0, 'negative', 'positive')}" ref="balance"/>

Exercise

Go back to the currencies XForm again.

Reminder: Don't forget to use instance('search') .

Hint: You should initialise <code> with EUR, otherwise initially the submit button will look odd.

The xforms-value-changed event

If you have a control bound to a value (i.e. with a ref), whenever that value changes, whether by this control or another, an xforms-value-changed event is sent to the control. So you can listen for that event, and respond to it.

<input ref="balance">
   <label>Balance</label>
   <action ev:event="xforms-value-changed">
      <setvalue ref="../changes" value=". + 1"/>
      <setvalue ref="../time"
                value="local-dateTime()"/>
   </action>
</input>

Exercise

Go back to the currencies XForm again.

Add an action for xforms-value-changed to the top select1. The action should do a <send/> for the submission.

Delete the <submit/> control.

Hint: you can put the action element after the itemset element.

Insert

You can add a new element to an instance using the <insert/> action:

<trigger>
   <label>+</label>
   <hint>Add an element</hint>
   <action ev:event="DOMActivate">
      <insert ref="buy"/>
   </action>
</trigger>

The default is just to duplicate the last element.

You may want to clear the content of the newly added element:

<trigger>
   <label>+</label>
   <hint>Add an element</hint>
   <action ev:event="DOMActivate">
      <insert ref="buy"/>
      <setvalue ref="buy[last()]"/>
   </action>
</trigger>

Origin

If the list is empty, an insert won't do anything, because there is no last element to duplicate. The more general case takes the element from elsewhere, for example a separate instance:

<instance id="blank">
   <data xmlns="">
      <buy/>
   </data>
</instance>

and then the action is:

<action ev:event="DOMActivate">
   <insert ref="buy" origin="instance('blank')/buy"/>
</action>

The default is to add it at the end of the list, but you can insert new elements anywhere. For example, before the first element:

<insert ref="buy[1]" position="before"
        origin="instance('blank')/buy"/>

Also "after".

Exercise

Add a trigger to add elements to the supplied To-do list XForm

Delete

Deleting elements is very similar. The only thing to watch out for is that

<delete ref="buy"/>

deletes ALL the buy elements. If there's more than one, and you want to delete just one, you have to specify which:

<delete ref="buy[1]"/>
<delete ref="buy[last()]"/>
<delete ref="buy[. = 'cheese']"/>

One way is to delete the current element:

<repeat ref="buy">
   <input class="inline" ref="."/>
   <trigger>
      <label>×</label>
      <hint>delete</hint>
      <action ev:event="DOMActivate">
         <delete ref="."/>
      </action>
   </trigger>
</repeat>

Exercise

Add a trigger to delete entries on the previous exercise.

readonly

When a value has a calculate property it becomes 'readonly'.

<bind ref="total" type="double" calculate="sum(../value)"/>

This means that if you attach it to an input control or similar, the control won't let you change the value.

While this is the default behaviour for calculate, it is also possible to make other values readonly if you need. Either always:

<bind ref="price" readonly="true()"/>

or conditionally, for example:

<bind ref="price" readonly="../edit != true()"/>

Exercise

Change the previous exercise so that once a task has been marked "done", the other fields are no longer editable.

Saving data

To save data, you use submission, this time to put data somewhere, rather than get it:

<submission id="save" \
            method="put" 
            resource="list.xml"
            ref="instance('list')"
            replace="none"/>

This saves the data in the instance into a file called list.xml.

To get it back, we do the reverse:

<submission id="restore" 
            method="get" 
            resource="list.xml"
            replace="instance" instance="list"
            serialization="none"/>

Be aware that not all servers accept the PUT method.

Exercise

Add submissions and submit controls to the previous exercise to save and restore.

For extra points, add handlers for xforms-submit-error.

Relevant and required data

You can specify the conditions under which certain data is relevant. For instance, a credit-card number is only relevant when paying by credit card:

<bind ref="cc" relevant="../payment='credit'"/>

In an address the state is only relevant if the country is the USA:

<bind ref="state" relevant="../country='USA'"/>

Relevance has two main effects:

  1. a control bound to a non-relevant value is not displayed,
  2. when a submit is done, by default non-relevant items are not submitted with the data (you can change this by using relevant="false" on the submission element).

It's worth noting that there's another sort of relevance as well: if a control is bound to an element that just isn't there (for instance if it's been deleted), the control is also not shown. If the element gets re-inserted, the control will return.

Required

A similar data property is required.

If a value is required and hasn't (yet) got a value, it is displayed in a different way.

By default you can't submit data containing empty required values (it gets reported as a validation error); again, you can override this with validate="false" on the submission element.

<bind ref="city" required="true()"/>
<bind ref="state" required="../country='USA'"/>

Relevance has priority over required: if something is required but not relevant, it's not a problem if it hasn't got a value.

Exercise

Change the previous example so that only tasks that have not been completed are shown.

Hint: The easiest way to see if a value x is true or not is with "x != 'true'" or "x != true()". This is because booleans can have three states: true, false, or not set.

Validity

You've seen how to bind a type to a value:

<bind ref="low" type="integer"/>

and how to require that a value is not empty:

<bind ref="name" required="true()"/>

There is a third property you can attach, a constraint:

<bind ref="low" type="integer" constraint=". > 1 and . &lt; ../high"/>
<bind ref="high" type="integer" constraint=". &lt; 100"/>

These three properties determine whether a value is valid or not. If a value is not valid, controls attached to it will be displayed accordingly.

Data that contains non-valid values cannot be submitted (unless you add validate="false" to the submission element).

Alert

Any control that is bound to a value can include an alert element to warn about validity:

<input ref="low" incremental="true">
   <label>low</label>
   <alert>must be an integer, above 1 and below <output ref="../high"/></alert>
</input>
<input ref="high" incremental="true">
   <label>high</label>
   <alert>must be an integer, below 100 and above <output ref="../low"/></alert>
</input>

Exercise

Add suitable alerts to the supplied XForm.

Extra points for making it adapt to the situation.

Hint: translate(a, b, '') returns the string a with all examples of the characters in b removed.

Dates

Dates and times have an interesting representation,

local-dateTime(): 2023-04-22T04:09:34+02:00
local-date(): 2023-04-22+02:00

The clever thing about this representation is that dates and dateTimes are lexically ordered, so you can compare them with the usual < and > operators.

For instance, you have a list of values recorded on certain dates, sorted on the date:

<values>
   <value date="2023-05-01">100</value>
   <value date="2023-06-19">98</value>
   <value date="2023-07-21">102</value>
   <value date="2023-08-23">101</value>
</values>

If you want to add a new value at the right place in the list, you insert it AFTER the elements whose dates are less:

<insert ref="value[@date &lt; instance('new')/value/@date]"
        position="after"
        origin="instance('new')/value"
/>

or equivalently before items whose dates are greater:

<insert ref="value[@date > instance('new')/value/@date]"
        position="before"
        origin="instance('new')/value"
/>

Exercise

Go back to the To-do list XForm, and add a class with an AVT to the output that shows up overdue tasks.

Hint: Use a CSS rule like ".overdue input {background-color: yellow}"

How to tell when an instance is changed

The save control for the To Do list allows you to save the data to a file, but is there some way to know whether it needs to be saved, in other words whether an instance has changed?

We've met the xforms-value-changed event already, and as we mentioned, it gets dispatched to the control that is bound to the value, so we put the handler in the content of that element:

<input ref="name">
   <label>Name</label>
   <action ev:event="xforms-value-changed">
      <setvalue ref="instance('admin')/changed">true</setvalue>
   </action>
</input>

Bubbling

However, many events 'bubble': from the element they are dispatched to they move up the tree of elements. So if we do this:

<action ev:event="xforms-value-changed">
   <setvalue ref="instance('admin')/changed">true</setvalue>
</action>
<input ref="name"><label>Amount</label></input>
<input ref="street"><label>Street</label></input>

then the action will receive the events from both inputs. It won't be able to tell them apart, but it will know that something has changed. So here we do that for our earlier example of a shopping list, with the action positioned within the repeat control:

<repeat ref="buy">
   <action ev:event="xforms-value-changed">
      <setvalue ref="instance('admin')/changed">true</setvalue>
   </action>
   ...

You have to be careful you put it so that you catch just the events you are interested in. If you put it above the <repeat> then you also catch events from controls at the same level as the repeat.

Changes for insertions and deletions

The events for elements being inserted and deleted are sent to the instance itself, and are xforms-insert and xforms-delete. Clearly, you can't put the action inside the instance content, since it would then appear to be part of the instance data, so you use the instance id:

<action ev:event="xforms-insert" ev:listener="list">

It otherwise works just the same. (In fact you can do this with all <action> elements if you wish). So now the complete picture is:

<action ev:event="xforms-insert" ev:listener="list">
   <setvalue ref="instance('admin')/changed">true</setvalue>
</action>
<action ev:event="xforms-delete" ev:listener="list">
   <setvalue ref="instance('admin')/changed">true</setvalue>
</action>
 ...
<repeat ref="buy">
   <action ev:event="xforms-value-changed">
      <setvalue ref="instance('admin')/changed">true</setvalue>
   </action>
 ...

Exercise

Go back to the To-do list XForm with the save button, and make it so that the save button's label indicates whether the data should be saved or not.

Hint: Don't forget to clear the changed value after a successful save or restore (listen for the xforms-submit-done event).

Warning: You may want to use <output value=""/> and not <output ref=""/> in the label, because a ref generates an xforms-value-changed event whenever its referenced value changes, and value="" doesn't.

Multiple selections

The <select> control was mentioned earlier. It is just like <select1> except that you can select several values, and they are combined into a string:

<select ref="shopping">
   <label>Shopping</label>
   <item>
      <label>Bread</label>
      <value>bread</value>
   </item>
   <item>
      <label>Butter</label>
      <value>butter</value>
   </item>
   ... etc. ...
</select>
<output ref="shopping"><label>Result</label></output>

Dynamic multiple selections

Just like with select1, you can get the items from an instance with <itemset> in exactly the same way:

<instance id="items">
   <data xmlns="">
      <buy>bread</buy>
      <buy>butter</buy>
      <buy>milk</buy>
      <buy>cheese</buy>
   </data>
</instance>
 ...
<select ref="shopping">
   <label>Shopping</label>
   <itemset ref="instance('items')/buy">
      <label ref="."/>
      <value ref="."/>
   </itemset>
</select>

Selected data

From a data-structuring point of view, instead of a result like

<shopping>Bread Bananas</shopping>

it can be far more useful to have structured data, like

<shopping>
   <buy>Bread</buy>
   <buy>Bananas</buy>
</shopping>

To achieve this we only have to change one thing: we change the <value> into <copy>:

<select ref="shopping">
   <itemset ref="instance('items')/buy">
      <label ref="."/>
      <copy ref="."/>
   </itemset>
</select>

Then when an item is selected, rather than just the value of the element being used, the whole element is used, including attributes and child elements. Selecting and deselecting is then like a restricted version of insert and delete.

This means that we also have to change how the result is output, from:

<output ref="shopping"/>

to:

<repeat ref="shopping/buy">
   <output ref="."/>
</repeat>

Exercise

Take the supplied example of the To Do List and create a <select> control for deciding which tasks to do today. Take the data from the to-do-list file. Use the name element for the labels.

Visibility

When we talked about relevance, we pointed out that controls that are not relevant are not displayed. You can put this to good use. For instance:

<instance>
   <data xmlns="">
      <visible>yes</visible>
   </data>
</instance>
<bind ref="visible" relevant=". = 'yes'"/>
 ...
<group ref="visible">
All content in this group is only visible when 'visible' has the value 'yes'.
</group>
<trigger>
   <label><output value="if(visible = 'yes', 'hide', 'show')"/></label>
   <action ev:event="DOMActivate">
      <setvalue ref="visible" value="if(. = 'yes', 'no', 'yes'"/>
   </action>
</trigger>

Switch

However, there is another way to control visibility: the <switch> control:

<switch>
   <case id="home">
      ...
   </case>
   <case id="products">
      ...
   </case>
   ...
</switch>

With this control, exactly one of the cases is visible at any time; by default initially the first one. To change cases, you use the action <toggle case="..."/>:

<trigger>
   <label>Home</label>
   <action ev:event="DOMActivate">
      <toggle case="home"/>
   </action>
</trigger>

Exercise

Take any To Do List, and make it so that the button to add a new task uses <switch> to expose a separate dialogue to enter details of the new task before adding it to the list. In that dialogue include an [add] button, and a [cancel] button.

Own events

It is also possible to dispatch and catch your own events. You dispatch them with the <dispatch> action to an element with a particular id:

<dispatch name="SAVE" targetid="element"/>

and catch them in the normal way.

For instance, if there are several places where you are catching an event and performing the same action, you could instead dispatch your own event to a central place to handle the event.

A good example is catching changes to an instance, which as you know can be reported by xforms-insert, xforms-delete, and xforms-value-changed events.

So you can do:

<dispatch name="CHANGE" targetid="M" ev:event="xforms-insert"/>
<dispatch name="CHANGE" targetid="M" ev:event="xforms-delete"/>
<dispatch name="CHANGE" targetid="M" ev:event="xforms-value-changed"/>

and then catch it in the model with id M:

<model id="M">
   ...
   <action ev:event="CHANGE">
      ...
   </action>
</model>

Delayed dispatches

It is possible to dispatch an event with a delay, measured in milliseconds:

<dispatch name="SAVE" targetid="M" delay="10000"/>

which will dispatch the SAVE event after 10 seconds. For instance, suppose we want to make a clock. At start up we send a TICK event:

<action ev:event="xforms-ready">
   <dispatch name="TICK" targetid="M"/>
</action>

We respond to that event by setting a value to the current time, and dispatching another TICK with a delay of a second:

<action ev:event="TICK">
   <setvalue ref="time" value="local-dateTime()"/>
   <dispatch name="TICK" targetid="M" delay="1000"/></action>

Then if we output the time value, it will update every second.

Exercise

In the last version of the To-do list you changed it so that the save button's label indicates whether the data should be saved or not.

Now make it so that if the button already indicates that the data should be saved, do nothing more, but otherwise change it and dispatch a SAVE event with a delay of let's say 10 seconds.

When you catch the SAVE event, if the button still indicates the data needs to be saved, save the data automatically.

What Next?

We've come to the end of this tutorial.

The next place to look is Introduction to XForms which has links to a number of larger examples of the development of XForms apps, such as games, calendars, the use of SVG, and several others.