4.4 Complete Example of an XForms User InterfaceWe conclude this chapter with a complete XForms example that uses nested repeating structures to create a dynamic task list. The resulting visual interface is shown in Figure 4.18. Figure 4.18. Task list interface rendered by X-Smiles. The task list tracks a set of open tasks. Tasks are organized into sections such as work or personal ; a user can add or remove sections as desired. A section consists of one or more tasks, and a task in turn holds information about a particular task, such as the date the task is due, a description of the task, and its current status. The task list interface we create allows the user to edit, maintain, and view the task list within a Web browser or other conforming XForms user agents . In creating this example, we cover many of the concepts introduced so far in this book including:
The example will be introduced in stages to cover each of these items. 4.4.1 Defining the Structure of the Task List The structure of the task list is defined by an XML Schema as shown in Figure 4.19. A task list has element Figure 4.19 XML Schema that defines the data model for the task list.< x:schema targetNamespace ="http://example.com/todo" xmlns:t ="http://example.com/todo" xmlns:x ="http://www.w3.org/2001/XMLSchema" elementFormDefault ="qualified" id ="todo-schema"> < x:element name ="task"> < x:complexType > < x:sequence > < x:element name ="date" type ="x:date"/> < x:element name ="description" type ="x:string"/> < x:element name ="status" type ="x:string"/> < x:element name ="done" type ="x:boolean"/> </ x:sequence > </ x:complexType > </ x:element > < x:element name ="section"> < x:complexType > < x:sequence maxOccurs ="unbounded"> < x:element ref ="t:task"/> </ x:sequence > < x:attribute name ="name" type ="x:string"/> </ x:complexType > </ x:element > < x:element name ="todo"> < x:complexType > < x:sequence maxOccurs ="unbounded"> < x:element ref ="t:section"/> </ x:sequence > </ x:complexType > </ x:element > </ x:schema > Element
We declare attribute id with value todo-schema for the schema definition, and this identifier will be used when connecting the instance declaration with the schema definition in the XForms model. The schema for the task list declares the target namespace for this schema in the statement targetNamespace="http://example.com/todo" to specify that elements defined by this schema are in the namespace identified by http://example.com/todo . The local namespace prefix t is used within the schema in referring to types defined in this schema, for example, t:task . Note that defining the instance data in an application-specific namespace is a powerful but fairly advanced feature. For the various elements being defined by this schema to be placed in the application-specific namespace, we add the attribute declaration elementFormDefault ="qualified" to the root of the schema element. Because of the way XML namespaces interact with XPath, XForms binding expressions need to namespace qualify individual components of the XPath locator. As an example, note the namespace prefix t: in each step of the XPath locators used when binding user interface controls. If we omitted the namespace prefix in this case, the binding expression would fail. Writing /todo/section/task would use the default namespace when looking for these nodes; as a consequence, todo would use the default namespace, which in the case of the user interface markup is different from the namespace that contains the task list. 4.4.2 Declaring the Task List InstanceNext , we create a skeleton XML instance for holding the task list. This skeleton serves as the initial data for the task list; it also serves as a declaration that is used when creating new sections and tasks in the task list. The instance declaration is shown in Figure 4.20. Figure 4.20 Instance declaration for the task list.< todo xmlns ="http://example.com/todo"> < section name ="business"> < task > < date >2003-01-07</ date > < description >XForms Call</ description > < status />< done /> </ task ></ section > < section name ="personal"> < task > < date >2003-04-15</ date > < description >Tax Deadline</ description > < status />< done /> </ task ></ section > < section name ="writing"> < task > < date >2003-04-01</ date > < description >Complete book</ description > < status />< done /> </ task ></ section > </ todo > The namespace for the task list is declared as the default namespace using the statement xmlns="http://example.com/todo" to avoid having to namespace qualify the various elements in the task list. The task list starts with three sections named work , personal , and writing . Each section contains one partially filled-in 4.4.3 Declaring the Data Model within XHTML |
schema | Identifies the schema that defines the type and structure of instance data to be used in this model. Here, we use #todo-schema to identify the schema for the task list via its unique identifier shown in Figure 4.19. |
The instance data is specified by reference in the line
< xf:instance src="todo-instance.xml"/>
where the location todo-instance.xml contains the skeleton instance shown in Figure 4.20. For brevity, we have replaced the schema definition for the task list shown in Figure 4.19 with a one-line comment.
In addition to the schema and instance declarations, the XForms model also declares a submission
element that specifies the what , where , and how of submit processing. The result of submitting will be to update the user's saved task list.
Finally, element head
contains a CSS style section in element
style
that contains CSS style rules for rendering specific aspects of the user interface. The CSS rules declared here will be used in Section 4.4.4 to style the various user interface controls.
The XForms user interface for editing and viewing the task list within an XHTML page can be thought of as a Web browser window onto the XML document shown in Figure 4.20. Thus, the actual document that the user is viewing and editing is the XML task list; the XForms user interface hosted in an XHTML page enables the user to maintain the task list without having to edit the raw XML source. In this section, we enable such browser-based editing by binding a user interface consisting of XForms constructs and controls to the task list data model; see Figure 4.22.
< xf:repeat xmlns ="http://www.w3.org/1999/xhtml" xmlns:t ="http://example.com/todo" xmlns:ev ="http://www.w3.org/2001/xml-events" xmlns:xf ="http://www.w3.org/2002/xforms" id ="rs" nodeset ="/t:todo/t:section"> < h2 >< xf:input ref ="@name">...</ xf:input ></ h2 > < table > < tr xf:repeat-nodeset ="t:task" xf:repeat-id ="rt"> < td >< xf:input ref ="t:date"> < xf:label >Date</ xf:label ></ xf:input ></ td > < td >< xf:input ref ="t:description"> < xf:label >Task</ xf:label ></ xf:input ></ td > < td >< xf:input ref ="t:status"> < xf:label >...</ xf:label ></ xf:input ></ td > < td >< xf:input ref ="t:done"> < xf:label >Done</ xf:label ></ xf:input ></ td ></ tr > < tr >< td >< xf:trigger ev:event ="DOMActivate"> < xf:label >New Task</ xf:label > < xf:insert at ="index('rt')" position ="after" nodeset ="/t:section[index('rs')]/t:task"/> </ xf:trigger ></ td > < td >< xf:trigger ev:event ="DOMActivate"> < xf:label >Delete Task</ xf:label > < xf:delete at ="index('rt')" nodeset ="/t:section[index('rs')]/t:task"/> </ xf:trigger ></ td > < td >< xf:trigger ev:event ="DOMActivate"> < xf:label >Scroll Forward</ xf:label > < xf:setindex repeat ="rt" index ="index('rt')+1"/></ xf:trigger ></ td > < td >< xf:trigger ev:event ="DOMActivate"> < xf:label >Scroll Back</ xf:label > < xf:setindex repeat ="rt" index ="index('rt')-1"/> </ xf:trigger ></ td ></ tr ></ table ></ xf:repeat >
A key feature of the task list is its hierarchical nature and the ability to grow and shrink dynamically. The task list has been organized into a set of logical sections comprising the various tasks. On the user interface side, we mirror this structure by creating XHTML sections that correspond to the sections in the task list. In doing so, we can use the section name as encapsulated by attribute name of element section
in constructing the section title that is displayed to the user. Since the number of sections can grow or shrink at run-time, XForms aggregation construct
repeat
is a natural choice for creating a scrolling view-port that allows the user to view a portion of the task list. We will create the necessary controls for adding, removing, and scrolling these sections and aggregate these controls to create navigation toolbars .
The tasks appearing in each section have additional substructure , and aligning the fields making up each task is an appropriate visual representation. We use XHTML tables to achieve this visual display. The user can add, remove, and scroll tasks appearing in a given section, and we use a repeating construct to achieve this end result. However, since we are using XHTML tables to render the list of tasks in a given section, we use repeat attributes rather than element repeat
to create XHTML tables that grow or shrink dynamically during user interaction.
The consequence of this design is that we create nested repeat structures, with the outer repeat
iterating the sections appearing in the task list and the inner repeat iterating tasks in a given section. Finally, notice that this form of nesting requires no special attention on the author's part; the design of the XForms repeat construct ensures that nested repeats work as the author would expect.
Note that for this example, we assume the XHTML namespace as the default; see relevant declaration in Figure 4.25. Hence, we prefix all elements from the XForms namespace via the local namespace prefix xf .
Outer repeat
. The outer
repeat
iterates over the
section
children of the task list and is identified by setting attribute id to rs . This unique identifier will be used when manipulating the state of this repeating structure via XForms actions
insert
,
delete
, and
setindex
.
We identify the node-set to iterate via the binding expression
nodeset="/todo/section"
which selects all section
children of element
todo
. Given the instance declared in Figure 4.20, this would select a set consisting of 3
section
nodes. Notice that since there is only one
model
in this complete example, we do not need to specify attribute model in the binding expression; by default XForms uses the first model in the document. The binding expression appearing on element
repeat
also establishes the XPath evaluation context when evaluating relative XPath locators within the body of element
repeat
.
For brevity, we have not set optional attributes of element repeat
such as startindex . In this case, the browser can display as many sections as appropriate for the connecting device.
The markup appearing within element repeat
declares the template user interface to be applied to each member of the node-set being iterated. We first create a section header via XHTML element
h2
that displays an editable field bound to attribute name of element
section
. Notice that XForms user interface control
input
is used as the contents of the XHTML section header created by element
h2
. The result is to create a Web page where the section headings are editable by the user. Within this
input
control, we use a relative XPath locator @name to address attribute name of the current
section
. In the current example, the instance shown in Figure 4.20 results in the creation of 3 XHTML
h2
elements, with each displaying attribute name from the corresponding
section
element. Editing the displayed value results in the underlying XML instance being updated, and the display is refreshed to show the new value.
The key thing to notice is that relative XPath locators appearing within the body of element repeat
are evaluated in the evaluation context established by the binding expression on the containing
repeat
and with the current member of the node-set being iterated as the context node. Thus, the binding expression on control
input
ref="@name"
is interpreted as ./@name , where . refers to the current node, that is, the current section
. This is key to understanding the functioning of nested repeat structures as we will see when we examine the inner repeat next.
Inner repeat. The outer repeat
creates an XHTML
table
for displaying the tasks in a given section. The context node for the outer
repeat
, that is, . refers to the current
section
from the task list, and this current
section
sets up the evaluation context as explained earlier. We want the XHTML table that displays the individual tasks to grow or shrink with the tasks in a section. We achieve this by turning XHTML element
tr
into a repeating structure via repeat attributes described in Section 4.3.6. In this case, the template user interface for this repeating construct is an XHTML table row that displays the fields making up an individual task.
The inner repeat is created via repeat attributes on XHTML element tr
with the line
<tr repeat-nodeset="task" repeat-id="rt">
which declares that the repeating structure being created will be identified via the unique identifier rt . The binding expression that locates the set of nodes to iterate uses the current evaluation context as set up by the outer repeat
. Thus, the XPath locator in the statement
repeat-nodeset="task"
is interpreted as ./task and selects all task
children of the current
section
. Thus, the outer
repeat
determines the specific
section
to operate on; the inner repeat structure selects the
task
of a specific
section
. The result is that, as the user changes the
section
that is current via add, delete, or scroll operations, the inner repeat structure is correctly updated to display the tasks from the right
section
.
The contents of the table row as given by the children of element tr
declare the template user interface that is applied to each task appearing in the collection being iterated. We have created individual table cells via XHTML element
td
for each of these fields. Each table cell contains an XForms user interface control that binds to the corresponding field in element
task
.
Adding, removing, and scrolling tasks Before closing off element table
created by the outer
repeat
, we create a table row that holds user interface controls for adding, removing, and scrolling tasks in the current section. These controls use XForms action handlers
insert
,
delete
, and
setindex
in a manner similar to that explained in Figure 4.13. Within these controls, we address the inner repeat via its unique identifier rt and the outer
repeat
via its unique identifier rs . Since the
section
on which the inner repeat operates is completely determined by the state of the outer
repeat
, the add, delete, and scroll controls in this example are identical in operation to what we saw when creating the shopping cart in Section 4.3.4.
Following, we examine one of these controls in detail to explain the functioning of the nested repeats. Consider the control that adds a new task. The new task is added by invoking XForms handler insert
. This invocation needs to specify the node-set to which the new
task
node should be added and the position at which it should be inserted. The node-set in question is the set of all
task
children of the current
section
.
The position of the current section
within the node-set of all
section
children of the task list can be accessed by invoking XPath extension function index , as in
index(rs)
which returns the value of the repeat index for the outer repeat
. This expression can in turn be used to locate the desired set of
task
children of the current
section
as in
nodeset="section[index('rs')]/task"
which selects the set of all section
nodes, filters it by the predicate index(rs) , and finally selects all
task
children of the result. Next, we specify where in this set of
task
elements the insertion should happen. This is expressed via the following:
xf:insert at="index('rt)" position="after"
where XPath extension function index is used to access the current value of the repeat index for the inner repeat identified by rt .
Finally, notice that the add, delete, and scroll controls described earlier appear within the outer repeat
. The effect is to create a toolbar consisting of these controls for each
section
of the task list.
Next, we examine the binding expressions used in the user interface controls in Figure 4.22. As before, the evaluation context and context node for evaluating these XPath locators are determined by the containing repeat
. The user interface in Figure 4.22 creates four input controls that each binds to one of the four child elements of
task
. The binding expressions in each of these controls are relative XPath expressions that are all evaluated with the current
task
as the context node. Thus, the binding expression on the first
input
control
<input ref="date">...</input>
binds that input control to the date
of the
task
node that is current , that is, the node at the position given by index('rt') .
Notice also that we have bound XForms control input
to each of the four child elements of
task
. Recall from the schema shown in Figure 4.19 that the children of
task
are typed . This type information can be used to create user interface controls appropriate to a given interaction modality and device. Thus, the
input
control bound to field
date
of type xsd:date might be rendered as a date picker control on a given device. The date picker on a desktop client might take the form of a simple calendar widget; the date picker on a cell phone with a small display might be rendered as a text field with smart completion. Child element
done
is of type xsd:boolean ; this information can be used to render the associated
input
control as a checkbox where appropriate.
Controls for adding, removing, and scrolling tasks are described in Section 4.4.4. The controls for adding, removing, and scrolling the sections in the task list are similar in structure and functionality. Placing these controls in element group
as shown in Figure 4.23 turns these controls into a conceptual toolbar that can be placed at the appropriate point in the containing user interface.
< group xmlns ="http://www.w3.org/2002/xforms" xmlns:ev ="http://www.w3.org/2001/xml-events" xmlns:t ="http://example.com/todo"> < trigger ev:event ="DOMActivate"> < label >New section</ label > < insert nodeset ="t:section" at ="index('rs')" position ="after"/></ trigger > < trigger ev:event ="DOMActivate"> < label >Delete section</ label > < delete nodeset ="t:section" at ="index('rs')"/> </ trigger > < trigger ev:event ="DOMActivate"> < label >Scroll Forward</ label > < setindex repeat ="rs" index ="index('rs')+1"/> </ trigger > < trigger ev:event ="DOMActivate"> < label >Scroll Back</ label > < setindex repeat ="rs" index ="index('rs')-1"/> </ trigger > </ group >
Notice that creating a new section in the task list effectively clones the prototypical section
element declared in Figure 4.20. Binding the user interface shown in Figure 4.22 to the new collection results in an additional section appearing in the user interface.
This newly created section has an empty task
node, and this
task
node in turns gets bound by the inner repeat structure that is part of the newly created table row. Notice also that depending on the available display real estate, a fixed number of sections will be shown to the user at any given time. Since the toolbar consisting of the controls for operating on sections is placed outside the outer
repeat
structure, that toolbar will never scroll off the display while the user is working with the task list.
The end result is to divide the displayed page effectively into a fixed toolbar and a scrolling region that displays the sections of the task list. Each displayed section is in turn subdivided into a toolbar for manipulating the tasks in that section and a scrolling view-port that displays the tasks in that section. We show a conceptual overview of this visual display in Figure 4.24.
In Figure 4.25, we bring together the various pieces of the task list example in an XHTML page that creates the complete task list application. Element html
is the root of this document, and it declares the XHTML namespace to be the default namespace. We declare local namespace prefixes for the various namespaces used by the task list application as follows :
< html xmlns ="http://www.w3.org/1999/xhtml" xmlns:xf ="http://www.w3.org/2002/xforms" xmlns:xsd ="http://www.w3.org/2001/XMLSchema" xmlns:todo ="http://example.com/todo" xmlns:ev ="http://www.w3.org/2001/xml-events"> <!-- insert head element here --> < body > < h1 >Task List</ h1 > <!-- insert section toolbar here. --> <!-- insert nested repeating here--> <!-- Insert task list summarizer here. --> <!--Insert submit control here--> </ body ></ html >
xf | The XForms 1.0 namespace. |
xsd | The XML Schema namespace corresponding to XML Schema 1.0. XML Schema became a W3C Recommendation in 2002. |
ev | The XML Events namespace. XML Events is currently a W3C Candidate Recommendation, but the namespace URI is not expected to change when XML Events becomes a W3C Recommendation. |
t | The namespace containing elements in the task list instance. This namespace is private to the task list application. |
Next, we insert XHTML element head
as shown in Figure 4.21. This contains the XForms
model
and the CSS style rules used to style various aspects of the presentation.
The user interface described in Section 4.4.4 and Section 4.4.6 appears within XHTML element body
. Notice that in the resulting interface, the level one heading produced by XHTML element
h1
is fixed with respect to the scrolling view-port created by the
repeat
structure that binds to the hierarchical task list.
We follow the repeat
that holds the task list user interface with other XForms controls that display a summary of the task list; see Figure 4.26. Like the title produced via element
h1
, this summary section is also fixed with respect to the scrolling task list.
< group xmlns ="http://www.w3.org/2002/xforms" xmlns:t ="http://example.com/todo"> You have < output value ="count(/t:todo/t:section)"/> sections in your task list with a total of < output value = "count(//t:task[ not(boolean-from-string(t:done))])"/> unfinished tasks.</ group >
The summary section displays the number of sections in the task list and a count of the tasks that remain to be completed. Since this computed information is not stored in a specific node in the task list instance, we cannot use a binding expression in control output
to look up this information. We therefore use attribute value [2] on control
output
to specify the expression to compute. This information is computed dynamically at run-time and updates as the user interacts with the task list.
[2] This was an oft- requested feature that was added following public feedback during the Last Call period.
We conclude the example by adding a submit
control that enables the user to save the task list.
We began this section by introducing the XML document that holds the task list in Figure 4.20. The primary purpose of the user interface we have created so far is to edit, maintain; and view this XML document within a standard browser interface without having to worry about the details of the XML markup. The interface we have created provides the user the necessary interface controls to view and manipulate the task list XML document. The key advantage with the interface we have created is that the underlying task list remains valid XML at all times. We can complete the circle by submitting the task list in order to save the current state.
Creating such save task list functionality requires the addition of a submit
control (see Figure 4.27) that binds to the
submission
element we created in the XForms
model
in Figure 4.21. Activating control
submit
results in the current state of the task list being serialized as an XML document and the result of this serialization being transmitted to the URI specified in the
submission
element.
< submit xmlns ="http://www.w3.org/2002/xforms" submission ="s01"> < label >Save Task List</ label >< help >...</ help > </ submit >