4.3 Repeating Structures with repeat
The ability to adapt dynamically to
Such repeating constructs are most commonly found when interacting with electronic stores on the Web today where the metaphor of a dynamic shopping cart is implemented using appropriate looping constructs in either client-side or server-side scripts. XForms construct repeat defines a declarative construct that can be used to iterate over collections of like nodes in the XForms model, for example, the items in a shopping cart.
4.3.1 Designing Construct repeat
The purpose of construct
is to create a user interface that
over a collection of nodes. To this end,
can be thought of as an
; contents of
can be thought of as a
of the user interface to be created for each node in the collection being iterated. The user interface created by construct
displays a portion of the collection being iterated with user interaction facilities that permit the user to scroll through the collection, and to add or delete nodes to the collection. Notice that all of these operations depend on the notion of a
4.3.2 Anatomy of Construct repeat
Attributes of Construct repeat
Child Elements of repeat
The body of construct
is the template user interface to be used when presenting each member of the collection. This can use all of the XForms user interface vocabulary
in addition to markup defined by the host language. Thus, when using XForms within XHTML, body of construct
might use XHTML markup in addition to markup defined by XForms. Conceptually, the contents of construct
can be thought of as being
4.3.3 Shopping Cart Using Construct repeat
Using the markup described so far, we define a shopping cart example that allows the user to add and remove products from a conceptual shopping cart interactively. The visual interface for the complete shopping cart example is shown in Figure 4.9.
Figure 4.9. X-Smiles rendering of the XForms shopping cart.
The shopping cart display grows or shrinks appropriately and displays a portion of the cart that includes the
at any given time. This example consists of two
Shopping Cart Model
For this example, we first define an XForms model that declares the structure of our shopping cart in Figure 4.10.
Figure 4.10 Shopping cart model defines the structure of element cart .
< model xmlns ="http://www.w3.org/2002/xforms" xmlns:xsd ="http://www.w3.org/2001/XMLSchema" id ="cart" schema ="cs.xsd"> < instance id ="c1"> < cart xmlns =""> <
This model uses the schema defined in Figure 4.11 to define the type and structure of the instance data used by the shopping cart. The shopping cart holds one or more line-item elements, with each line-item containing details about a given item being purchased.
Figure 4.11 Schema for the shopping cart.
< x:schema id ="cart-schema" xmlns:x ="http://www.w3.org/2001/XMLSchema"> < x:element
Notice that this model includes two instances, the first to hold the items on the shopping cart, and the second to hold a product catalog; contrast this with the similar example on dynamic selections in Section 3.4.5 where we used separate models for the
Observe the following facts with respect to the model shown in Figure 4.10:
Shopping Cart User Interface
The markup shown in Figure 4.12 binds an XForms user interface to the shopping cart model. We use construct repeat to iterate the line-item elements in the shopping cart. Attribute startindex is set to 1 to cause the user interface to display a portion of the cart starting with the first line-item element; attribute number is set to 3 to serve as a presentation hint. The markup appearing within body of element repeat contains XForms user interface controls for populating an individual line-item in the cart.
Figure 4.12 Shopping cart user interface using construct repeat .
< group xmlns ="http://www.w3.org/2002/xforms"> < label >Shopping Cart</ label > < repeat id ="cartUI" model ="cart" nodeset ="/cart/line-item" startindex ="1" number ="3"> < select1 ref ="item" appearance ="minimal"> < label >Select Product</ label > <!-- Namespace qualify bindings.--> < itemset nodeset ="instance('cat')/product"> < label > Item: < output ref ="description"/> Price: < output ref ="price"/> </ label > < copy ref ="."/> </ itemset ></ select1 > < input ref ="quantity"> < label >Quantity</ label ></ input > Price + Shipping: < output ref ="cost"/> </ repeat ></ group >
Control select1 is used within the body of repeat to enable the user to select a product from the catalog. We have used appearance to request a minimal presentation, that is, one that takes up minimal display real estate. A visual user agent might choose to render this control as a pull-down list. This is appropriate, since the user will be presented with this control once for each line-item appearing in the shopping cart.
Binding attributes on control select1 specify that the value selected will be placed in the shopping cart at location
The available products are rendered via element itemset whose binding expression refers to the catalog instance to determine the available products at run-time. Note that this binding expression uses function instance as in
to address the set of products listed in the catalog. Child elements label and copy also use this binding expression to set up the XPath context for evaluating relative XPath locators.
creates the list of alternatives as described in Section 3.4.5. Notice that in this example, we have used element
to build up a label for each alternative that
to declare that picking one of the alternatives should result in the
operated on by the containing
By definition, the product contained in line-item/item in the shopping cart has the same type and structure as the product appearing in the catalog; thus, the copy operation populates the shopping cart with structurally valid content.
The remainder of the template user interface in
The markup shown in Figure 4.12 does not contain presentational or stylistic information. In a real-world shopping cart, this markup would be hosted in an appropriate host language, for example, XHTML; this would provide the necessary constructs for aligning the controls appearing within the body of repeat or styling them via CSS.
We will extend this example in a later chapter by extending the model to define cardinality constraints on the number of items that can be placed in the shopping cart. These constraints can then be used in enabling or disabling the add and delete controls shown in Figure 4.13. The shopping cart is contained within construct group that has an appropriate label. This group will be used next in aggregating the repeat with the add, delete, and scroll controls.
Figure 4.13 Adding add , delete , and scroll controls to shopping cart.
< group xmlns ="http://www.w3.org/2002/xforms" xmlns:ev ="http://www.w3.org/2001/xml-events"> < label >Shopping Cart</ label > < repeat id ="cartUI">...</ repeat > < group model ="cart" ref ="/cart"> < label >Shopping Cart Toolbar</ label > < trigger id ="addItem"> < label >Create Item</ label > < insert nodeset ="line-item" at ="index('cartUI')" position ="after" ev:event ="DOMActivate"/></ trigger > < trigger id ="del"> < label >Remove Item</ label > < delete nodeset ="line-item" at ="index('cartUI')" ev:event ="DOMActivate"/></ trigger > < trigger id ="forward"> < label >Scroll Forward</ label > < setindex repeat ="cartUI" index ="index('cartUI')+1"/></ trigger > < trigger id ="back"> < label >Scroll Back</ label > < setindex repeat ="cartUI" index ="index('cartUI')-1"/></ trigger > </ group ></ group >
4.3.4 Adding Controls to the Shopping Cart
Next, we add add , delete , and scroll controls to the shopping cart. These controls will use XForms declarative event handlers insert , delete , and setindex , respectively. We first describe these event handlers before using them within control trigger to create the desired controls.
Event Handlers for Use with repeat
Event handlers insert and delete enable the addition and deletion of nodes to a collection of nodes. In this sense, they are not specific to construct repeat ; however, in XForms 1.0 they are mostly used in conjunction with user interfaces created via repeat . Handler setindex is specific to repeat since it manipulates the index that determines the current node for a construct repeat . Invoking any of these handlers causes the user interface created via construct repeat to be updated appropriately to reflect the new state of the underlying collection.
insert and delete Handlers insert and delete are symmetric with respect to their underlying design as well as the XML markup they expose. These handlers need to specify the following items of information:
Add, Delete, and Scroll Controls
Here, we use actions
controls for the shopping cart. For this example, we will place these controls outside the body of construct
to create a conceptual
for the shopping cart. This toolbar will be placed inside a
that carries the relevant metadata for these controls. The toolbar and the user interface created via
Observe the following in the example shown in Figure 4.13:
4.3.5 User Interaction with Construct repeat
We now review the various stages of user interaction with the shopping cart as created so far. The shopping cart starts off with one empty
when the user first
In rendering the template user interface specified within the body of construct repeat for the line-item in the cart, the XForms client creates a select1 control with no product selected and fields that bind to the quantity and cost fields. Field quantity has an initial value of 1 as specified in the model. Next, the user activates control select1 to pick a specific product using the pull-down list. Selecting a product causes the corresponding product node from the catalog to be copied to the shopping cart.
is no longer empty. The
control now displays the description of the selected product. When the shopping cart model is updated with this selected product, that update in turn triggers the computation of field
. The shopping cart model has defined the value of
calculate="../item/product/price *../quantity +../item/product/price * ../quantity * 0.08 +../item/product/shipping"/>
This expression is evaluated to produce the value to be stored in cost . The user interface is updated to reflect the updated value of field cost . Next, the user changes the value of quantity from 1 to 5. This again causes field cost to be recomputed and the user interface to be refreshed appropriately.
Next, the user adds a new line-item to the shopping cart by activating the
control. This invokes handler
The user proceeds to add new
entries to the shopping cart. As the number of
entries grows beyond 3, the presentational hint provided via attribute
takes effect, and the user interface
4.3.6 Using Construct repeat within XHTML Tables
When presenting the shopping cart user interface within an XHTML page, each line-item might be presented as a row of a table. The obvious solution would be to place element repeat within an XHTML table element and have the repeat produce the rows of the table; see Figure 4.14.
Figure 4.14 Placing element repeat within element table produces invalid XHTML.
< table xmlns ="http://www.w3.org/1999/xhtml" xmlns:xf ="http://www.w3.org/2002/xforms"> <!-- XHTML does not permit this --> < xf:repeat id ="cartUI" model ="cart" nodeset ="/cart/line-item" startindex ="1" number ="3"> < tr > < td > < xf:select1 ref ="item" appearance ="minimal"> < xf:label >Select Product</ xf:label > < xf:itemset nodeset ="instance('cat')/product"> < xf:label > Item: < xf:output ref ="description"/> Price: < xf:output ref ="price"/> </ xf:label > < xf:copy ref ="."/> </ xf:itemset ></ xf:select1 > </ td > < td > < xf:input ref ="quantity"> < xf:label >Quantity</ xf:label ></ xf:input > </ td > < td > Price + Shipping: < xf:output ref ="cost"/> </ td ></ tr > </ xf:repeat ></ table >
However, doing so would produce an invalid XHTML document; this is because as defined, the content model of XHTML element
cannot be extended. To cover this common use case while
The repeat attributes encode the same set of information as encapsulated by element repeat , and repeating structures created via repeat attributes have the same functionality as repeating structures created via element repeat . Thus, the difference is one of concrete syntax, and there is no change to the underlying processing model.
To understand this alternative syntax for creating repeating structures, think of repeat as encapsulating its contents in an anonymous group ”anonymous because in the case of element repeat , this group element does not appear in the markup as seen in Figure 4.12.
Viewed this way, element repeat is present in the markup to carry the various information items needed to create the repeating structure; it achieves this by encoding the needed information via attributes appearing on element repeat . Next, we make the anonymous group explicit as shown in Figure 4.15. The repeating structures thus created are identical by our definition.
Figure 4.15 Element repeat contains an anonymous group element.
< repeat xmlns ="http://www.w3.org/2002/xforms" id ="cartUI" model ="cart" nodeset ="/cart/line-item" startindex ="1" number ="3"> <!--Anonymous group made explicit --> < group > < select1 ref ="item" appearance ="minimal"> < label >Select Product</ label > < itemset nodeset ="instance('cat')/product"> < label > Description: < output ref ="description"/> Price: < output ref ="price"/> </ label > < copy ref ="."/> </ itemset ></ select1 > < input ref ="quantity">< label >...</ label ></ input > Price + Shipping: < output ref ="cost"/> </ group ></ repeat >
Finally, we can move the attributes appearing on element repeat to element group before dropping the containing repeat element as shown in Figure 4.16.
Figure 4.16 Moving repeat attributes to the containing anonymous group.
< group xmlns ="http://www.w3.org/2002/xforms" repeat-id ="cartUI" repeat-model ="cart" repeat-nodeset ="/cart/line-item" repeat-startindex ="1" repeat-number ="3"> <!-- cart UI as before --> </ group >
There is one final twist; we prefix each attribute name in the repeat attributes with repeat- for clarity. See Table 4.1 for the correspondence between attributes of element repeat and the repeat attributes .
Table 4.1. Correspondence between Repeat Attributes and Attributes of Element repeat
The XForms working group defined repeating structures via an element (rather than via attributes) to ensure future extensibility. This is why the element version of repeating structures is still the preferred solution over the alternative of placing
. However, XForms is also designed to be hosted in a variety of markup languages such as XHTML and SVG. As
Figure 4.17 XHTML tables that grow and shrink dynamically.
< table xmlns ="http://www.w3.org/1999/xhtml" xmlns:xf ="http://www.w3.org/2002/xforms"> <!--Repeat via attributes on element tr--> < tr repeat-id ="cartUI" repeat-model ="cart" repeat-nodeset ="/cart/line-item" repeat-startindex ="1" repeat-number ="3"> < td > < xf:select1 ref ="item" appearance ="minimal"> < xf:label >Select Product</ xf:label > < xf:itemset nodeset ="instance('cat')/product"> < xf:label > Item: < xf:output ref ="description"/> Price: < xf:output ref ="price"/> </ xf:label > < xf:copy ref ="."/> </ xf:itemset ></ xf:select1 ></ td > < td > < xf:input ref ="quantity"> < xf:label >Quantity</ xf:label ></ xf:input ></ td > < td > Price + Shipping: < xf:output ref ="cost"/> </ td ></ tr ></ table >
To summarize, repeating structures can now be encoded by placing repeat attributes as defined earlier on any markup element and consequently on element tr appearing inside XHTML tables. This allows the creation of XHTML tables that grow or shrink dynamically while preserving the structural validity of XHTML documents.
4.3.7 Summary of Construct repeat
We conclude this section with a summary of construct repeat . Element repeat is similar in functionality to iterator constructs found in modern object-oriented languages like Java. Construct repeat enables the creation of browser-based user interfaces for editing and manipulating XML structures.
The children of element
define a user interface template that is instantiated for the members of the collection over which the
member of this collection and is key to the functioning of the repeating construct. Access to this
Special XForms actions insert , delete , and setindex can be used to manipulate the underlying collection. XForms user interface controls can be used to view and update individual members of the collection. All such changes to the underlying collection are reflected in the user interface, and this creates dynamic user interfaces.
This design enables the nesting of repeating structures to create complex user interfaces. We detail one such use in Section 4.4 where we use nested, repeating structures to maintain a hierarchical task list. The preferred means of encoding repeating structures is via element repeat . In addition, XForms 1.0 defines a set of repeat attributes that provide the same functionality as element repeat for use in container languages where the content model does not permit new elements.