Binding it Together
Some of you may have begun to visualize the letters MVC floating in the backgroundnot as some 1940s Roman epic movie (or 1990s remake) but as the initials for Model/View/Controller. In the early 1970s, the language Smalltalk introduced this particular design architecture. Simply put, the MVC architecture supposes you have a discrete model containing, some kind of output view, and then code of some sort to bind the view to the modela controller that handled event input and the mappings between the two.
XForms is very nearly a pure embodiment of MVC. The distinction between model and view (or user interface) is fairly clean. The binding mechanism, though, can seem a little confusing at first, because XForms actually relies on a few different effects to handle the binding.
At the very lowest level, you have the ref
attribute, which tells the XForm engine to associate a particular element or attribute value in the model with the control. For instance, in the Cost Per Share
section of the example form, you have the output tag:
attribute contains an XPath expression, usually pointing to one or more elements within the model. In the above case, the XPath expression uses the stock value as retrieved from the XPath expression /stockData/account/stock/value
. XForms passes the retrieved value to the XPath function format-number
, which converts the price into a string of the form '$#,##0.00'
, a number mask indicating that the string should include the US dollar symbol, with a separator at the thousands point (Continental readers would of course use a different mask).
In this particular case the reference in the ref
attribute is read only. However, references can go both ways. For instance, the sample XForm also inserts the initial value of the /stockData/account/shares
element into the Number of Shares
input box item.
When users change this field, nothing happens until they press the button (trigger) marked "Calculate".
element is XForms' closest approximation to a subroutine. Actions take their context based upon the elements that called them (or the elements that they are in), and the specific event. For instance, a trigger invokes a "DOMActivate" event when pressed (activated), causing any and all internal DOMActivate actions contained within to run. It is possible, though not recommended, to have multiple event handlers with the same event key.
command uses the ref
attribute to retrieve an element or attribute from the model, then replaces the current value of the reference with the evaluated XPath expression in the value
attribute. In this particular instance, the form takes the number of shares entered by the user, and multiplies that by the currently selected stock's value. Finally, it formats and displays the result as a floating point number with two significant displayed digits.
The selection box is more complex:
When the user makes a selection, XForms invokes the event xforms-value-changed
on the selection box, which then invokes the three <setvalue>
statements, changing the symbol, the stock value, and the total cost based upon this price and the number of outstanding shares. Changing the values automatically notifies all interface elements bound to them so they can update their own content, thus the <output>
elements change their displayed values automatically.
This may seem like a lot of work for what should be a fairly simple application, but there are some real advantages. Handling cascading values, where changing one control causes other controls to change their internal contentstheir modelsas well, is notoriously difficult. But in this case, the XForms engine acts as the notifier, handling all the uglier notification processes, and leaving developers free to concentrate on establishing the data paths and other resources. This has the advantage of making the code easier to maintain as well.