XBL Tutorial
Encapsulation
The component system favors a strong encapsulation so that components can be:
developed in isolation, without knowing the details of the application using them
used without knowing the details of how they are implemented
in short: reused as much as possible!
The goal is first eased of use and transparency for the form author. This means that sometimes the component author must do a little bit more work!
The default: strong encapsulation
By default, within an <xbl:binding>
element, encapsulation is strong: this means that XForms controls, models, and event handlers cannot:
see ids of the XForms document using the component
have access to the XPath context outside the component
In other words, things behave as if you were working in a new, completely separate XForms document!
If you place models within <xbl:implementation>
or <xbl:template>
, the same rule that applies in a top-level XForms document apply:
The default XPath context starts with the root element of the first instance of the first model.
However, if your component doesn't have a model, then the XPath context is set to an empty document node.
NOTE: In the future, this might change to being an empty sequence. For implementation-dependent reasons, for now an empty document node had to be used.
Creating a single-node binding
Orbeon Forms provides direct support for bindings with the binding
mode:
This automatically means that the component supports all the XForms binding attributes, including ref
and bind
, and UI events such as xforms-enabled
/ xforms-disabled
and others are dispatched as needed.
You can access the actual bound node via the xxf:binding()
function:
The id passed must be the id of the xbl:binding
element.
For more, see The binding mode.
Adding support for a value
Some native XForms controls, like xf:input
, xf:textarea
, etc. support holding a value. This means in particular that the control is able to dispatch xforms-value-changed
events. Some controls on the other hand, like xf:group
, xf:repeat
, can't hold a value and don't dispatch xforms-value-changed
events.
The default for XBL components is that they don't hold a value. Use the value
mode to add value behavior:
The xxf:value()
function allows access to the control's value from the inside, by using the binding id:
Adding LHHA elements
Orbeon Forms provides direct support for label, help, hint and alert (LHHA):
This automatically adds support for LHHA to the component:
By default, markup is output for the LHHA elements. You can disable this with the custom-lhha
mode:
With this mode, no markup is output, and the component author can access the LHHA values with XPath functions:
[SINCE Orbeon Forms 4.5] When using the lhha
mode, it is possible to link the label handled by the XBL engine to an internal control, so that that control and the label are linked. This is done with the xxbl:label-for
attribute:
A basic component
You can find the component discussed in this section in the Orbeon Forms distribution:
XBL:
tutorial-input.xbl
Example:
xbl-tutorial-input.xhtml
Orbeon Forms modes make this very simple:
Using local state
You can find the component discussed in this section in the Orbeon Forms distribution:
XBL:
xbl/orbeon/tutorial-davinci/tutorial-davinci.xbl
Example:
apps/xforms-sandbox/samples/xbl-tutorial-davinci.xhtml
Now assume you would like to write a component which stores the string of characters entered by the user but back to front, i.e. reversed. If the user types "Amelia", the string stored in the instance will be "ailemA".
How would you go about this? The binding between an XForms control and instance data is direct: the value entered by the user is stored into the instance as soon as the user moves out of the field, and you can't just write a transformation in between. So we need some intermediate state to store the value entered by the user.
To do so, we create a local instance. You can put it under the <xbl:implementation>
or <xbl:template>
elements in your XBL file:
Here we store a single value, so we just use a single root element in the instance: <value>
.
The local input field just points to the local instance instead of pointing to the external single-node binding:
So here, the input field points to the <value>
element.
NOTE: You could also write ref="."
, which would work because, like at the top-level of an XForms document, the default XPath context is the root element of the first instance in the first model. Using instance()
is a bit more explicit.
What is needed now is, when the local value changes, to copy it to the external single-node binding. You do so with an event handler:
What's missing now is to reverse the value:
Finally, the opposite operation is needed: when the component first comes to life, and when the external value changes, the internal value must update:
So here you go: you have a fully working non-trivial component:
Last updated