XBL - Guide to Using and Writing XBL Components

Contents

  1. 1 Introduction
    1. 1.1 Why are components needed?
    2. 1.2 Use cases
    3. 1.3 Terminology
  2. 2 Mini-FAQ
    1. 2.1 What can component do?
    2. 2.2 What does a component implementation look like?
    3. 2.3 How do you use a component?
    4. 2.4 Why not implement components in Java?
    5. 2.5 Why use XBL and not simply XSLT?
    6. 2.6 What's special about the XBL 2 implementation in Orbeon Forms?
  3. 3 Getting started
    1. 3.1 Copying an existing component
  4. 4 Mini-tutorial
    1. 4.1 Encapsulation
    2. 4.2 The default: strong encapsulation
    3. 4.3 Why and how to break the encapsulation?
    4. 4.4 Creating a single-node binding
      1. 4.4.1 With Orbeon Forms 4
      2. 4.4.2 With Orbeon Forms 3.9
    5. 4.5 Adding support for a value
      1. 4.5.1 With Orbeon Forms 4
      2. 4.5.2 With Orbeon Forms 3.9
    6. 4.6 Adding LHHA elements
      1. 4.6.1 With Orbeon Forms 4
      2. 4.6.2 With Orbeon Forms 3.9
    7. 4.7 A basic component
      1. 4.7.1 With Orbeon Forms 4
      2. 4.7.2 With Orbeon Forms 3.9
    8. 4.8 Using local state
  5. 5 XForms models
    1. 5.1 Placement of local models
    2. 5.2 Local model-related events
    3. 5.3 Construction and destruction of local models
  6. 6 Event handling
    1. 6.1 Event propagation
      1. 6.1.1 With Orbeon Forms 4
      2. 6.1.2 With Orbeon Forms 3.9
    2. 6.2 Component user: attaching event handlers to the bound node
    3. 6.3 Component user: nested content under the bound node
    4. 6.4 Component author: hooking-up creation and destruction event handlers
    5. 6.5 Component author: dispatching events from within the component
    6. 6.6 Component author: listening for events dispatched to the component
    7. 6.7 Component user: dispatching events to the component
  7. 7 CSS selectors
  8. 8 Level of support
  9. 9 Extensions 
    1. 9.1 xxbl:container attribute
    2. 9.2 xxbl:attr attribute
    3. 9.3 xbl:template/xxbl:transform attribute
    4. 9.4 xxbl:global element
    5. 9.5 xxbl:mirror attribute
  10. 10 Form Builder metadata
  11. 11 Conventions
    1. 11.1 Parameters 
  12. 12 JavaScript
    1. 12.1 Define a class for your component
    2. 12.2 Call methods of your class on XForms events
    3. 12.3 Read-only parameters
    4. 12.4 Sending events from JavaScript
  13. 13 Aspects of components
  14. 14 Other topics to address

Introduction

Why are components needed?

The Orbeon Forms XForms engine proposes out of the box a set of controls, including input fields, radio buttons, etc. Those are typically implemented natively within the XForms engine.

Beyond the basic set of controls, there is an obvious need for creating new reusable controls. It would be difficult to modify the XForms engine itself. Orbeon Forms therefore proposes a complete framework inspired by the XBL 2 specification to address this need.

Use cases

You can use components to implement:
  • Controls for datatypes which have a native implementation, but where a custom appearance is required
    • E.g. custom control for entering an xs:date with dropdown menus rather than a date picker
  • Controls for datatypes which do not have a native implementation
    • E.g. control to capture the xs:duration type
  • Controls which do not have a standard XML type
    • E.g. phone number control
  • Higher-level components
    • Instance inspector component
    • Grid table component
    • Google Maps component
This is not an exhaustive list. Your imagination is the limit!

See the documentation on already implemented components to get a feel for what is possible.

Terminology

  • Component: a piece of software which provides reusable behavior and presentation.
  • Component instance: a particular use of a component within an XForms document. A component might have multiple instances in a given page.
    • NOTE: This should not be confused with XForms instances.
  • Component implementation: the code which constitutes the inner workings of a particular component.
  • Component author: the person who writes a component.
  • Component user: the person who uses a component.
    • In general, writing a component will be harder than using one.
    • Obviously the user can be the same as the author!


Mini-FAQ

What can component do?

A lot! Among other things, they can:
  • behave like regular XForms controls (such as <xforms:input>), including handling bindings, labels, and events
  • keep local state using local models and instances
  • run their own submission to talk to the outside world, with <xforms:submission>
  • integrate with JavaScript code, for example to expose cool widgets to XForms authors
  • copy and/or modify XForms and HTML markup provided by the component user
  • use nested components

What does a component implementation look like?

That's the cool part: a component implementation looks very much like regular XForms within HTML!

A component is really a way of packaging and reusing a piece of XForms and HTML markup. So when writing a component, you can leverage all your knowledge of XForms and HTML.

How do you use a component?

This is even easier: each component is assigned an element name. For example, must built-in Orbeon Forms components are in the "fr:" namespace (for Form Runner), and so you write things like:

<fr:fields-date ref="my-date">
  <xforms:label>Birth Date</xforms:label>
</fr:fields-date>

Most components try to follow the XForms philosophy: for example, they use common attributes like ref, and usually allow nesting <xforms:label>, <xforms:help>, etc.

Why not implement components in Java?

This could be attractive, but then:
  • A component author would have to know Java, how to run a compiler, deploy the code, etc.
  • Orbeon Forms would have to define APIs to provide access to the low-level workings of the components. This is a lot of work!
  • We think it is much easier to write HTML and XForms!
  • This would raise the question of which  template language to use to produce the resulting markup. On the other hand, XBL is its own template language, and you can even combine it with XSLT.

Why use XBL and not simply XSLT?

Using XSLT to implement components is not always a good alternative:
  • Components may be used (instantiated) multiple times, and identifiers (the "id" attribute often used in HTML and XForms) must be handled accordingly:
    • Each component instantiation must produce unique ids in the browser
    • Id resolution must depend on where ids are used
    • Ids in different repeat iterations must be unique as well
  • XSLT does not enforce any encapsulations rules, including:
    • Visibility of objects from inside or outside a component
    • Containment of event flows within the component
  • Local models and instances are better handled natively
    • Each instantiation requires new models and new instances 
    • Within repeats, model/instance duplication occurs at runtime
Using XBL addresses all these issues.

Convinced? If not, read on! In the section about extensions, you'll find out that you can embed XSLT transformations within Orbeon Forms XBL components.

What's special about the XBL 2 implementation in Orbeon Forms?

  • Orbeon Forms components are inspired by XBL 2. XBL in this case is implemented server-side, not on the client!
  • Support for components is implemented at the XForms engine level.
  • Components can therefore be used within XForms pages, but not within non-XForms page (such as plain XHTML pages).
  • Because the XBL specification does not detail how it can be used in conjunction with XForms, Orbeon Forms uses XBL in a particular way, but it is not necessarily the only possible way.
  • Orbeon Forms implements a superset of a subset of XBL!


Getting started

Copying an existing component

The best thing to do is to start looking at existing components:
  • If you are working with the Orbeon Forms sources, they are located under:
    • src/resources-packaged/xbl
  • If you are working with a binary distribution:
    • unzip orbeon-resources-private.jar
    • the components are under the xbl directory
The "meat" of most components is in files ending with the .xbl extension.

To create your own component:
  • create a new xbl directory under your RESOURCES directory
  • create a directory with your company or project name (e.g. acme; Orbeon uses orbeon)
  • create directory with your new component name (e.g. cool-stuff)
  • create a new XBL file with the same name in that directory, e.g. cool-stuff.xbl
  • so you should have: xbl/acme/cool-stuff/cool-stuff.xbl
Then:
  • copy into your XBL file the content of a simple existing component, like xbl/orbeon/tutorial-simple/tutorial-simple.xbl
  • modify the binding rule ("fr|tutorial-simple") into something that matches your component name ("fr|cool-stuff")
  • within an XForms page
    • declare xmlns:fr="http://orbeon.org/oxf/xml/form-runner"
    • use the control with something like: <fr:cool-stuff ref="my-node"/>
    • when running your XForms page, you should see an upload field appear!
NOTE: In your own components, you should not use the "fr:" namespace, but instead you should use your own namespace to avoid naming conflicts.


Mini-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.

Why and how to break the encapsulation?

There are cases where a component can live in its own little world. For example, consider a component such as the Twitter Timeline component included with Orbeon Forms: it obtains its data through the Twitter API, and simply displays it. It doesn't need to access data or interact with the XForms document in which it is used (although you could imagine such interactions).

But in other cases, components need access to the outside world! For example, XPath expressions to evaluate within the XPath context in scope where the component is used.

Creating a single-node binding

With Orbeon Forms 4

Orbeon Forms 4 provides direct support for bindings with the binding mode:

<xbl:binding id="fr-foo" element="fr|foo" xxbl:mode="binding">

This automatically means that the component supports all the XForms binding attributes:
  • model
  • context
  • ref
  • bind
When a component has a binding, UI events are dispatched depending on the bound item:
  • xforms-enabled/disabled
  • xforms-readonly/readwrite
  • xforms-optional/required
  • xforms-valid/invalid
You can access the actual bound node via the xxforms:binding() function:

xxf:binding('fr-foo')

The id passed must be the id of the xbl:binding element.

The xxforms:binding-context() function returns the XPath evaluation context of the binding

xxf:binding-context('fr-foo')

With Orbeon Forms 3.9

Here is how you create a single-node binding in an XBL template:

<xbl:template>
    <xforms:group xbl:attr="model context ref bind" xxbl:scope="outer">
        ...
    </xforms:group>
</xbl:template>

This is what the code does:
  • It creates an XForms group within the XBL template.
  • It copies, with xbl:attr="..." all the single-node binding attributes present on the tag written by the component user (e.g. <fr:cool-stuff>) : model, context, ref and bind.
    • In the case of <fr:cool-stuff ref="my-node, you end up with <xforms:group ref="my-node">.
    • If any attribute is not present, it is simply not copied.
  • It adds a special attribute, xxbl:scope="outer", to indicate that the evaluation scope for the XPath expressions and ids on that element is the scope outside the component.
So what does this get us? A group, within the component, which is bound to an instance data node as indicated by the user of the component.

Now within that group, you might want to access resources, such as controls, instances, etc., which are local to the component again. To achieve this, you use xxbl:scope="innner":

<xforms:group xxbl:scope="inner">
    ....
</xforms:group>

Now one issue with this is: how do you get, from an inner scope, access to something in the outer scope? Typically, you do this with variables:

<xforms:group xbl:attr="model context ref bind" xxbl:scope="outer">
    <xforms:group xxbl:scope="inner">
        <!-- Variable pointing to external single-node binding -->
        <xxforms:variable name="binding" as="node()?">
            <xxforms:sequence select="." xxbl:scope="outer"/>
        </xxforms:variable>
        ...
    </xforms:group>
</xforms:group>

The variable named binding above:
  • Is defined within the inner scope.
  • Is evaluated in the outer scope.
Presto, this gives you access, within the component's private universe, to the external single-node binding specified by the component user.

NOTE: You notice, based on the example above, that XBL itself doesn't know anything about single-node bindings: instead, you create such bindings using a group, XForms constructs, and XBL extensions.

Adding support for a value

With Orbeon Forms 4

[SINCE: 2012-11-20]

Some native XForms controls, like xforms:input, xforms: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 xforms:group, xforms: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:

<xbl:binding
  id="fr-phone-binding"
  element="fr|us-phone"
  xxbl:mode="lhha binding value">

The xxforms:value() function allows access to the control's value from the inside, by using the binding id:

<xbl:binding id="fr-gaga" element="fr|gaga" xxbl:mode="binding value">
    <xbl:template>
        <xf:output id="gaga-output" value="xxf:value('fr-gaga')"/>
    </xbl:template>
</xbl:binding>

With Orbeon Forms 3.9

This is not supported with Orbeon Forms 3.9.

Adding LHHA elements

With Orbeon Forms 4

Orbeon Forms 4 provides direct support for label, help, hint and alert (LHHA):

<xbl:binding id="fr-foo" element="fr|foo" xxbl:mode="lhha">

This automatically adds support for LHHA to the component:

<fr:foo>
    <xforms:label>My label</xforms:label>
    ...
</fr:foo>

By default, markup is output for the LHHA elements. You can disable this with the custom-lhha mode:

<xbl:binding id="fr-foo" element="fr|foo" xxbl:mode="lhha custom-lhha">

With this mode, no markup is output, and the component author can access the LHHA values with XPath functions:

<xforms:output value="xxforms:label('fr-foo')"/>

[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:

<xbl:binding
  id="fr-dropdown-select1"
  element="fr|dropdown-select1"
  xxbl:container="span"
  xxbl:mode="lhha binding value"
  xxbl:label-for="select1">

<xbl:template>
    <xf:select1 appearance="minimal" ref="xxf:binding('fr-dropdown-select1')" id="select1">
    ...

With Orbeon Forms 3.9

Typical XForms controls feature <xforms:label>, <xforms:help>, (shortened to LHHA for "label", "help", "hint", and "alert") etc. You can easily copy the ones specified by the user within the XBL component:

<xforms:group xbl:attr="model context ref bind" xxbl:scope="outer">
    <xbl:content includes="xforms|label,xforms|help,xforms|hint,xforms|alert"/>

What this does is place the elements within the group. If the user writes:

<fr:cool-stuff ref="my-node">
  <xforms:label>Enter your philosophical thought</xforms:label>
</fr:cool-stuff>

The result, within the XBL component, will be:

<xforms:group ref="my-node" xxbl:scope="outer">
    <xforms:label>Enter your philosophical thought</xforms:label>
   
    ...
</xforms:group>

NOTE: <xbl:content> uses a CSS selector notation instead of XPath.

A basic component

You can find the component discussed in this section in the Orbeon Forms distribution:
  • XBL: xbl/orbeon/tutorial-input/tutorial-input.xbl
  • Example: apps/xforms-sandbox/samples/xbl-tutorial-input.xhtml

With Orbeon Forms 4

Orbeon Forms 4 modes make this very simple:

<xbl:xbl xmlns:xh="http://www.w3.org/1999/xhtml"
         xmlns:xf="http://www.w3.org/2002/xforms"
         xmlns:xs="http://www.w3.org/2001/XMLSchema"
         xmlns:ev="http://www.w3.org/2001/xml-events"
         xmlns:xxf="http://orbeon.org/oxf/xml/xforms"
         xmlns:fr="http://orbeon.org/oxf/xml/form-runner"
         xmlns:xbl="http://www.w3.org/ns/xbl"
         xmlns:xxbl="http://orbeon.org/oxf/xml/xbl">

    <xbl:binding element="fr|tutorial-input" id="fr-tutorial-input" xxbl:mode="lhha binding value">
        <xbl:template>
            <!-- Input points to the external single-node binding -->
            <xf:input ref="xxf:binding('fr-tutorial-input')"/>
        </xbl:template>
    </xbl:binding>
</xbl:xbl>


With Orbeon Forms 3.9

A super-simple component template with just one <xforms:input> will look like this:

<xbl:template>
    <xforms:group xbl:attr="model context ref bind" xxbl:scope="outer">
        <xbl:content includes="xforms|label,xforms|help,xforms|hint,xforms|alert"/>
        <xforms:group xxbl:scope="inner">
            <!-- Variable pointing to external single-node binding -->
            <xxforms:variable name="binding" as="node()?">
                <xxforms:sequence select="." xxbl:scope="outer"/>
            </xxforms:variable>
            <!-- Input points to the external single-node binding -->
            <xforms:input ref="$binding"/>
        </xforms:group>
    </xforms:group>
</xbl:template>

To complete that component, you must place it within an XBL file, complete with namespace declarations:

<xbl:xbl xmlns:xhtml="http://www.w3.org/1999/xhtml"
         xmlns:xforms="http://www.w3.org/2002/xforms"
         xmlns:xs="http://www.w3.org/2001/XMLSchema"
         xmlns:ev="http://www.w3.org/2001/xml-events"
         xmlns:xxforms="http://orbeon.org/oxf/xml/xforms"
         xmlns:fr="http://orbeon.org/oxf/xml/form-runner"
         xmlns:xbl="http://www.w3.org/ns/xbl"
         xmlns:xxbl="http://orbeon.org/oxf/xml/xbl">

    <xbl:binding element="fr|tutorial-input">
        <xbl:template>
            ...
        </xbl:template>
    </xbl:binding>
</xbl:xbl>

In the above:
  • <xbl:xbl> is the root element of any XBL file.
  • <xbl:binding> determines what kind of elements will trigger the component. Here, we settle on <fr:tutorial-input>.
    • Note again the CSS selector notation in the elements attribute, instead of the XPath notation.

The Orbeon XBL files are placed undeer the xbl/orbeon directory and in the "fr:" namespace and are automatically included.

However your own XBL files need to be included explicitly in your page, e.g.:

<xhtml:head>
    <!-- Main XForms model -->
    <xforms:model/>
    <xi:include href="oxf:/xbl/acme/cool-stuff/cool-stuff.xbl" xxi:omit-xml-base="true"/>
</xhtml:head>

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 inbetween. 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:

<xf:model>
    <xf:instance><value/></xf:instance>
</xf:model>

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:

<xf:input ref="instance()">

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:

<xforms:input ref="instance()">
    <xforms:setvalue
        ev:event="xforms-value-changed"
        ref="xxf:binding('fr-tutorial-davinci')"
        value="context()"/>
</xforms:input>

What's missing now is to reverse the value:

<xf:input ref="instance()">
    <xf:setvalue event="xforms-value-changed"
                 ref="xxf:binding('fr-tutorial-davinci')"
                 value="codepoints-to-string(
                          reverse(
                            string-to-codepoints(
                              instance())))"/>
</xf:input>

Finally, the opposite operation is needed: when the component first comes to life, and when the external value changes, the internal value must update:

<xbl:handler event="xforms-enabled xforms-value-changed"
             ref="instance()"
             value="codepoints-to-string(
                      reverse(
                        string-to-codepoints(
                          xxf:binding('fr-tutorial-davinci'))))"/>

So here you go: you have a fully working non-trivial component:

<xbl:xbl xmlns:xh="http://www.w3.org/1999/xhtml"
         xmlns:xf="http://www.w3.org/2002/xforms"
         xmlns:xs="http://www.w3.org/2001/XMLSchema"
         xmlns:ev="http://www.w3.org/2001/xml-events"
         xmlns:xxf="http://orbeon.org/oxf/xml/xforms"
         xmlns:fr="http://orbeon.org/oxf/xml/form-runner"
         xmlns:xbl="http://www.w3.org/ns/xbl"
         xmlns:xxbl="http://orbeon.org/oxf/xml/xbl">

    <xbl:binding element="fr|tutorial-davinci" id="fr-tutorial-davinci" xxbl:mode="lhha binding value">
        <xbl:handlers>
            <!-- When the control comes to life or its value changes, copy the new value to the local
                 model after 
reversing it -->
            <xbl:handler event="xforms-enabled xforms-value-changed"
                         ref="instance()"
                         value="codepoints-to-string(
                                  reverse(
                                    string-to-codepoints(
                                      xxf:binding('fr-tutorial-davinci'))))"/>
        </xbl:handlers>
        <xbl:template>
            <!-- Local model and instance -->
            <xf:model>
                <xf:instance><value/></xf:instance>
            </xf:model>
            <!-- Input points to the internal value -->
            <xf:input ref="instance()">
                <!-- When the local value changes, copy it to the external single-node binding after
                     reversing it -->
                <xf:setvalue event="xforms-value-changed"
                             ref="xxf:binding('fr-tutorial-davinci')"
                             value="codepoints-to-string(
                                      reverse(
                                        string-to-codepoints(
                                          instance())))"/>
            </xf:input>
        </xbl:template>
    </xbl:binding>
</xbl:xbl>


XForms models

Placement of local models

A component can have its own set of XForms models, called local models. For each instance of the component, a new copy of the models is made, so that component instances behave completely independently from each other.

With XForms 1.1, the standard convention is to place your models under the <xhtml:head> element (even though some new model scoping rules are being developed for Orbeon Forms). An XBL component does not have an <xhtml:head> element, so Orbeon Forms supports placing models in two places:
  • Under the <xbl:implementation> element. In this case, models are identical for all instances of a particular component.
  • Under the <xbl:template> element. In this case, models can be different depending on the component instance, since the XBL template can parametrize its elements and attributes.
NOTE: In XBL, the purpose of <xbl:implementation> is to place new methods and properties. Orbeon Forms uses XForms as the implementation or "scripting" language of XBL components, so it does not have methods and properties. But models, through events, can implement behavior, so allowing model placement under <xbl:implementation>seems to fit the intent of XBL.

Local model-related events

XForms 1.1 specifies the following event sequence upon value changes, insertions, etc.:
  • rebuild (if document structure changed): update binds structure/dependencies
  • recalculate (if value changed): perform MIPs and calculated values updates 
  • revalidate (if value changed): perform instance revalidation
  • refresh: updates the UI and dispatch UI events
XBL components with local models are no different and they receive these events when needed.

Construction and destruction of local models

It is possible to use components within repeats. These rules apply:
  • Models go through initialization when a sub-tree of controls is created.
    • Situations:
      • initial tree of control is initialized
      • a repeat iteration is bound to a new node
      • NOTE: future optimization works plans that this will also happen in all situations where controls go from non-relevant to relevant
    • Events dispatched:
      • xforms-model-construct
      • xforms-model-construct-done
      • however, note that xforms-ready is NOT dispatched.
  • Models go through destruction when a sub-tree of controls is deleted.
    • Situations:
      • a repeat iteration is no longer bound to a node
      • NOTE: future optimization works plans that this will also happen in all situations where controls go from relevant to non-relevant
    • Events dispatched:
      • xforms-model-destruct
  • Otherwise models do not get new construction/destruction events when iterations change


Event handling

Event propagation

XBL promotes a strong encapsulation of data and behavior. In particular events which target elements within the component typically are invisible to the component user.

With Orbeon Forms 4

[SINCE  2012-06-08]
  • Events flow along XBL scopes.
  • The Orbeon Forms 3.9 and earlier retargeting behavior is entirely removed.
  • The xxforms:event() function is deprecated and always returns the original event.
  • For special use cases like fr:error-summary, phantom handlers are introduced.
  • DOMFocusIn and DOMFocusOut still follow the behavior outlined in XForms - Focus.

With Orbeon Forms 3.9

With Orbeon Forms 3.9 up to 2012-06-08 builds, the following applies:
  • Some events, like DOMActivate and other user interface events, may be retargeted, which means that for the component user they appear as if they were targeting the bound node.
  • When events are retargetted over an XBL component boundary, the extension XPath function xxforms:event() allows accessing the original event information.
  • In practice, this should impact few existing components.

Component user: attaching event handlers to the bound node

By default, you get the same behavior you have with built-in controls, like xforms:input

Event handlers can directly observe elements with XBL bindings:

<foo:bar>
    <xforms:action ev:event="xforms-value-changed">
        ...
    </xforms:action>
</foo:bar>

The action handler above observes events going through element <foo:bar>, assuming <foo:bar> has an XBL binding.

Similarly, the following observes events targeted directly to <foo:bar>:

<foo:bar>
    <xforms:action ev:event="xforms-enabled" ev:target="#observer">
        ...
    </xforms:action>
</foo:bar>

Note that to achieve this the XBL engine does some special work here:
  • it recognizes those nested handlers
  • it attaches them to the bound node
  • it hides them from the component's xbl:content processing
    • NOTE: However these handlers are still visible from XSLT processing when using xxbl:transform
With Orbeon Forms 4.0, you can disable this automatic processing of nested event handlers, although you should only need this for very special components:

<xbl:binding id="fr-foo" element="fr|foo" xxbl:mode="nohandlers">

It is also possible to attach handlers by id, like with any XForms control:

<foo:bar id="my-foobar">
    ...
</foo:bar>
...
<xforms:action ev:event="xforms-value-changed" ev:observer="my-foobar">
    ...
</xforms:action>

NOTE: As of 2009-10, with Orbeon Forms, only standard XForms controls and elements which have an XML binding can be used as event observers. Other elements, such as an HTML <div>, cannot be event observers.

Component user: nested content under the bound node

Some components, such as a tabview, in effect behave like XForms grouping controls (like xforms:group, xforms:switch/case, xforms:repeat). With such components, a lot of content is typically nested under the bound node:

<fr:tab>
    <xh:div>
        <xf:group>
            <xforms:action ev:event="DOMActivate">
                ...
            </xforms:action>
        </xf:group>
    </xh:div>
</fr:tab>

It is up to the component author to handle nested content properly. When using xbl:content, the XBL engine does the work for you:
  • the nested content is automatically visible from the "outside"  of the component
    • ids and variables are visible across the bound node (here fr:tab)
  • events flow nicely as the form author would expect when using a regular XForms grouping control

Component author: hooking-up creation and destruction event handlers

Since xforms-ready is not dispatched to local models, here is how you can register handlers to perform initializations when the component is created and destroyed:

<xbl:template>
    <xforms:group id="component-group">
        <xforms:action ev:event="xforms-enabled" ev:target="component-group">
            <!-- Perform construction here -->
            <xxforms:script>...</xxforms:script>
        </xforms:action>
        <xforms:action ev:event="xforms-disabled" ev:target="component-group">
            <!-- Perform destruction here -->
            <xxforms:script>...</xxforms:script>
        </xforms:action>
        ... Rest of component ...
    </xforms:group>
</xbl:template>


Note the ev:target attributes, which ensure that only events actually targeting this particular group are handled. If you omit that attribute, you might observe more than one event for creation or destruction, which is in general not desired.

Component author: dispatching events from within the component

This allows a component to send information to the outside world.

A component can dispatch events to its bound element by using xforms:dispach and using the id of the xbl:binding element as target.

Example:

<xbl:binding id="foobar-component" element="fr|foobar">
    <xbl:template>
        <!-- Local controls -->
        <xforms:trigger id="internal-trigger-1">
            <xforms:label>Dispatch outside</xforms:label>
            <xforms:dispatch ev:event="DOMActivate" name="my-event" target="foobar-component">
                <xxforms:context name="fr:my-context" select="42"/>
            </xforms:dispatch>
        </xforms:trigger>
    </xbl:template>
</xbl:binding>

The component user can listen to this event as expected, for example:

<fr:foobar id="my-foobar">
    <xforms:message ev:event="my-event"><xforms:output value="concat('Got it: ', event('fr:my-context'))"/></xforms:message>
</fr:foobar>

The use of the "fr:" prefix in the event context information is not mandatory: you can use your own namespace prefix. However, it is good practice to use a prefix so as to prevent name conflicts with standard XForms event context information.

Component author: listening for events dispatched to the component

This allows a component to receive information from the outside world.

You can register event handler attached to the bound node inside your component with the xbl:handlers/xbl:handler elements:

<xbl:binding id="fr-bar" element="fr|bar">
    <xbl:handlers>
        <!-- Handlers are attached to the bound node -->
        <xbl:handler event="my-event" phase="target">
            <xforms:setvalue model="model" ref="value1" value="event('fr:one')"/>
            <xforms:setvalue model="model" ref="value2" value="event('fr:two')"/>
        </xbl:handler>
    </xbl:handlers>

The xbl:handler element looks very much like an xforms:action element. In particular, it supports the following attributes:
  • event: specifies which event(s) to listen to.
    NOTE: Like for ev:event, Orbeon Forms supports as an extension a list of space-separated event names.

  • phase: whether to call the handler upon the capture, target, or bubble phase.
The xbl:handler element can contain one or more XForms actions.

[TODO: In JavaScript, we can only dispatch events to a model. So will dispatch to dispatch the event to a model inside the component (see below for more on that), which in turn can used the method above to dispatch the event to the component.]

Component user: dispatching events to the component

The following example responds to a button being activated and dispatches an event with custom context information to an fr:bar component:

<fr:bar id="my-bar"/>

<xforms:trigger>
    <xforms:label>Insert</xforms:label>
    <xforms:dispatch ev:event="DOMActivate" name="my-event" targetid="my-bar">
        <xxforms:context name="fr:one" select="'Red'"/>
        <xxforms:context name="fr:two" select="'Blue'"/>
    </xforms:dispatch>
</xforms:trigger>


When the event my-event reaches the component, it activates the event handler registered with xbl:handler. That handler has access to the custom context information using the event() function.

Event handlers are attached to the bound node but they execute within the context of the component, which means that they have access to XForms elements declared within the component. This includes:
  • xforms:model elements declared within xbl:implementation
  • xforms:model elements declared within xbl:template
  • controls declared within xbl:template

CSS selectors

The xbl:content element allows copying elements which are descendant elements from the bound node. The selectors work as if applied to an XML document rooted at the bound node.

Say you have this markup:

<fr:inline-input ref="name">
    <xforms:label>Name</xforms:label>
</fr:inline-input>

The implementation of the fr:inline-input component can copy the nested xforms:label element under a group as follows:

<xforms:group>
    <xbl:content includes="xforms|label"/>


The CSS selection expression above is exactly equivalent to writing in XPath:

descendant-or-self::xforms:label

Note the difference of notation in XML/XPath and in CSS to refer to qualified element names:
  • XML/XPath uses a "colon" character: foo:bar
  • CSS uses a "pipe" character: foo|bar
Both XPath and CSS are expression languages allowing selecting nodes from XML documents, but they have a quite different syntax!

Now here is a more complex scenario:

<fr:link-select1 ref="gender">
    <xforms:label>Gender</xforms:label>
    <xforms:itemset nodeset="instance('genders')/gender">
        <xforms:label ref="label"/>
        <xforms:value ref="value"/>
    </xforms:itemset>
</fr:link-select1>

You would think that the implementation of the fr:link-select1 component could simply copy the nested xforms:label element as follows:

<xforms:group>
    <xbl:content includes="xforms|label"/>

But this doesn't work properly because the CSS selector xforms|label actually returns all descendant label elements, including the xforms:label element under xforms:itemset.

As of 2012-06-05, the recommend way to express this is as follows:

<xforms:group>
    <xbl:content includes=":root > xforms|label"/>

The :root pseudo-class refers to the bound element (here fr:link-select1). The > combinator "describes a childhood relationship between two elements", like the XPath / axis. So the result is equivalent to the XPath:

/*/xforms:label

NOTE: Prior to 2012-06-05 builds, you had to write the following:

<xforms:group>
    <xbl:content includes="fr|link-select1 > xforms|label"/>

The drawback of that method was that the CSS expression could catch nested fr|link-select1 elements. This was not an issue with fr|link-select1 specifically, but could be an issue in other cases, such as fr|tabview, which allows nested content. Further, repeating the name of the bound element was error prone.


Level of support

Supported element and attributes from the XBL 2 specification:
  • xbl:xbl
  • xbl:binding/@element
    • only simple CSS selectors (see above)
  • xbl:implementation
    • may contain local XForms models
  • xbl:handlers/xbl:handler
    • @event attribute (with extension: space-separate list of event names)
    • @phase attribute: "capture" | "target" | "bubble"
    • @propagate
    • @default-action
  • xbl:template
  • xbl:content without @includes
  • xbl:content/@includes attribute
    • only simple CSS selectors (see below)
  • @xbl:attr attribute
  • xbl:resources
    • xbl:style
  • xbl:script
Unsupported element and attributes from the XBL 2 specification:
  • xbl:xbl
    • @script-type and xbl:xbl/@style-type are ignored
  • xbl:binding
    • @extends
  • xbl:implementation
  • xbl:template
    • @apply-author-sheets
    • @allow-selectors-through
  • xbl:content
    • @apply-binding-sheets
    • @locked
  • xbl:inherited
  • @xbl:pseudo
  • xbl:div
  • xbl:handlers/xbl:handler
    • @trusted
    • all keyboard- and mouse-related attributes
  • xbl:resources
    • xbl:prefetch

Extensions 

All the generic extensions are in the namespace http://orbeon.org/oxf/xml/xbl, and the usual mapping of this namespace is xmlns:xxbl="http://orbeon.org/oxf/xml/xbl".

xxbl:container attribute

The xxbl:container attribute on xbl:binding allows specifying the name of the HTML element that the XBL binding uses to encapsulate content. By default, this is xhtml:div. Here is how to change it to xhtml:span:

<xbl:binding id="my-binding" element="fr|my-binding" xxbl:container="span">

xxbl:attr attribute

The standard xbl:attr attribute does not support accessing attributes other than those positioned exactly on the bound element. This is a serious limitation. The Orbeon Forms implementation adds an extension attribute, xxbl:attr,  which takes an XPath expression:

xxbl:attr="xforms:alert/(@context | @ref | @bind | @model)"

xbl:template/xxbl:transform attribute

When more flexibility is needed than XBL can provide, the xxbl:transform attribute is your friend.

<xbl:template xxbl:transform="processorName">
    Transformation (in line)
</xbl:template>

Orbeon Forms runs the processor specified by this attribute connecting its config input to the content of the xbl:template and its data input to the bound element and replaces the content of the xbl:template by the data output of the processor. The most frequent expected use is to run XSLT transformations. For instance, to create a widget that alternates styles in table rows within an xforms:repeat:

<xbl:binding id="foo-table-alternate" element="foo|table-alternate">
    <xbl:template xxbl:transform="oxf:xslt">
        <xsl:transform version="2.0">
            <xsl:template match="@*|node()">
                <xsl:copy>
                    <xsl:apply-templates select="@*[not(name() = ('style1', 'style2'))]|node()"/>
                </xsl:copy>
            </xsl:template>
            <xsl:template match="foo:table-alternate">
                <xhtml:table>
                    <xsl:apply-templates select="@*|node()"/>
                </xhtml:table>
            </xsl:template>
            <xsl:template match="xforms:repeat/xhtml:tr" >
                <xxforms:variable name="position" select="position()"/>
                <xforms:group ref=".[$position mod 2 = 1]">
                    <xhtml:tr xbl:attr="style=style1">
                        <xsl:apply-templates select="@*|node()"/>
                    </xhtml:tr>
                </xforms:group>
                <xforms:group ref=".[not($position mod 2 = 1)]">
                    <xhtml:tr xbl:attr="style=style2">
                        <xsl:apply-templates select="@*|node()"/>
                    </xhtml:tr>
                </xforms:group>
            </xsl:template>
        </xsl:transform>
    </xbl:template>
</xbl:binding>

This widget can be invoked through:

<foo:table-alternate class="gridtable" style1="background: red" style2="background: white">
    <xhtml:tr>
        <xhtml:th>Label</xhtml:th>
        <xhtml:th>Value</xhtml:th>
    </xhtml:tr>
    <xforms:repeat nodeset="item">
        <xhtml:tr>
            <xhtml:td>
                <xforms:output value="@label"/>
            </xhtml:td>
            <xhtml:td>
                <xforms:output value="@value"/>
            </xhtml:td>
        </xhtml:tr>
    </xforms:repeat>
</foo:table-alternate>

The xbl:template/@xxbl:transform="oxf:xslt" attribute specifies that its child element (xsl:transform) is considered as an XSLT transformation, runs against the bound element (foo:table-alternate), and the result of this transformation is used as the actual content of the xbl:template.

The result of your transformation will often contain XBL attributes, and in this sample xbl:attr attributes are used to set the style of the rows with: <xhtml:tr xbl:attr="style=style1">.

The result of the transformation has to be a (well formed) single rooted XML fragment. This might seem obvious, but that means that you might have to encapsulate the sub elements of xbl:template in an XHTML div or  span compared to what you would have done if you were not applying a transformation.

The transformation has full access to the bound element and can transform any of its child nodes or attributes. This is the case even when bound elements (or widget references To keep things as encapsulated as possible and not change the behavior of bound elements that are potentially embedded, it is a good practice to define tight transformation templates that affect only the nodes that are meant to be transformed. In the example given above, one might for instance argue that  <xsl:template match="foo:table-alternate/xforms:repeat/xhtml:tr"> would be safer than <xsl:template match="xforms:repeat/xhtml:tr">.

You can use <xsl:message terminate="yes"> to report to the user errors that occur during the XSLT transformation. For example:

<xsl:message terminate="yes">Terminating!</xsl:message>

This results in an error will be output in the log, and the following error message will show in the browser:


xxbl:global element

The <xxbl:global> element allows an XBL binding to place global markup that is included in a page only once.

<xbl:xbl>
    <xbl:binding element="fr|foo">
        <xxbl:global>
            <!-- The single global dialog -->
            <xxforms:dialog id="my-dialog" level="modal" model="my-dialog-model">
                <xforms:label>My Global Dialog</xforms:label>

                
<!-- Dialog model -->
                <xforms:model id="my-dialog-model">
                    ...
                </xforms:model>

                ...

            </xxforms:dialog>
        </xxbl:global>
        <xbl:template>
            ...
        </xbl:template>
    </xbl:binding>
</xbl:xbl>

How this works:
  • The global markup is included at the end of the top-level XForms document, as if you put it there by hand.
  • If the XBL binding is not used, the global markup is not included.
  • Ids on elements in global markup must be made unique by the component author, as those ids become global as well.
The component can dispatch events to global controls if the outer scope is the top-level scope, with the xxbl:scope="outer" attribute. For instance, if in the <xxbl:global> you defined an <xxforms:dialog id="my-dialog">, then from within the component, you can run the following action:

<xxforms:show dialog="my-dialog" xxbl:scope="outer"/>

NOTE: A future enhancement to this feature might restrict id and XPath scope of global markup to that of the XBL binding.

xxbl:mirror attribute

[SINCE 2013-01-23]

This attribute, placed on a local XForms instance, tells the XBL engine to automatically mirror changes between that instance and the XBL component's bound node.

For mirroring to work, the XBL component must either:
  • if it has an XPath node binding: be bound to an element node
  • if it doesn't have an XPath node binding: be in the XPath context of an element node (done for compatibility with Form Builder section templates)
At most one instance in a given XBL component may have xxbl:mirror="true".

Lifecycle:
  • when the XBL component becomes relevant
    • the XBL instance is first initialized as usual
    • then if the XBL binding or context points to an element, that element is extracted to become the root element of a new element, which replaces the XBL instance
  • when updates (value changes, inserts, deletes) take place on the XBL instance, these changes are mirrored outside
  • when updates (value changes, inserts, deletes) take place outside, these changes are mirrored on the XBL instance
Example:

<xbl:binding id="fr-gaga" element="fr|gaga" xxbl:mode="binding">
    <xbl:implementation>
        <xf:model id="gaga-model">
            <xf:instance id="gaga-instance" xxbl:mirror="true">
                <empty/>
            </xf:instance>
        </xf:model>
    </xbl:implementation>
</xbl:binding>

Form Builder metadata

Conventions

Whenever it is possible, XBL components should follow patterns found in XForms controls. For instance, if it makes sense to think that the component is bound to a node, then the component should support single node binding attributes on the component element, just like an XForms control would.

Parameters 

A number of component take "parameters" that can be specified by users. Consider the existing date picker component. You bind it to a node which contains the date entered by the user, but can also provide a minimum and maximum date. We call those min/max dates parametersParameters can be:
  • Read-only – they only provide a value to the component, as in the above case of the min/max dates.
  • Read/write – the component can update a value stored in a an instance.
The convention dealing with parameters is as follows:
  • For read-only parameters, users can provide:

    • A static value through an attribute:
      • the attribute name is the parameter name;
      • the attribute value is the parameter value.

        <fr:date ref="..." mindate="1970-01-01"/>

    • A dynamic value through a nested element:
      • the element local name is the parameter name;
      • the element namespace is the namespace if the component;
      • the element supports single node binding attributes;
      • if the parameter is read-only, the element supports the value attribute like XForms output control does.

        <fr:date ref="...">
            <fr:mindate ref="/parameters/mindate"/>
        </fr:date>

  • For read/write parameters, users bind the component to the node from which to read/write the value with:

    • An attribute:
      • the attribute name starts with ref- followed by the parameter name;
      • the attribute value is a binding expression.

        <fr:map selected-longitude-ref="/coordinates/longitude"/>

    • An element:
      • which works like the element for dynamic values in the read-only case (see above).

        <fr:map>
            <fr:selected-longitude ref="/coordinates/longitude"/>
        </fr:map>


JavaScript

Define a class for your component

The XBL component shipped with Orbeon Forms are stored in their own directory under /xbl/orbeon. For instance, all the files for the currency component are under /xbl/orbeon/currency. To include a JavaScript file, use the <xbl:script> element directly inside the <xbl:xbl>:

<xbl:xbl>
    <xbl:script src="/xbl/orbeon/currency/currency.js"/>
    <xbl:binding id="fr-currency" element="fr|currency">
        ....
    </xbl:binding>
</xbl:xbl>

In the JavaScript file corresponding to your component, declare a class as follows:

YAHOO.namespace("xbl.fr");
YAHOO.xbl.fr.Currency = function() {};
ORBEON.xforms.XBL.declareClass(YAHOO.xbl.fr.Currency, "xbl-fr-currency");
YAHOO.xbl.fr.Currency.prototype = {

    attribute1: null,
    attribute2: null,

    init: function() {
        ...
    },

    valueChanged: function() {
        ...
    },
   
    ...
};

  • YAHOO.namespace("xbl.fr") defines a namespace for your class. All the XBL components components that ship with Orbeon Forms are in the xbl.fr namespace. If you are defining a component for your company or project named Acme, you could use the namespace xbl.acme.

  • ORBEON.xforms.XBL.declareClass() defines your class as an XBL class:
    • It takes 2 parameters: your class, and the CSS class found on the most HTML element that contains the markup for your components. This element is generated by Orbeon Forms, and the class name is derived from the id of your <xbl:binding>; for instance, if the id is fr-currency, the class name is xbl-fr-currency.
    • It adds a static method to your class called instance(). It is a factory method, which you will use to get or create an object corresponding to the "current" component (more on this later).
    • It define a static container attribute. In your JavaScript code, you can refer to this.container to retrieve the most outer HTML element corresponding to your component. For instance, if you know you have an input with the class xbl-fr-currency-xforms-input inside your component, you get the HTML element corresponding to that input with:

      YAHOO.util.Dom.getElementsByClassName("xbl-fr-currency-xforms-input", null, this.container)[0];

Call methods of your class on XForms events

You can call a JavaScript method defined in your JavaScript class when an XForms event occurs. For instance, to call the init() method when on xforms-enabled, write:

<xxforms:script ev:event="xforms-enabled">YAHOO.xbl.fr.Currency.instance(this).init();</xxforms:script>

The instance() method acts as an object factory for your component: it returns an instance of your class corresponding to the "current" component. It creates an instance of your class as necessary, and keeps track of existing objects, maintaing a 1-to-1 mapping between instances of the XBL component in the form and instance of your JavaScript class.

Read-only parameters

So your JavaScript can access the current value of parameters and be notified when their value changes, include the oxf:/oxf/xslt/utils/xbl.xsl XSL file, and call xxbl:parameter() function for each parameter, as in:

<xbl:xbl>
    <xbl:script src="/xbl/orbeon/currency/currency.js"/>
    <xbl:binding id="fr-currency" element="fr|currency">
        <xbl:template xxbl:transform="oxf:unsafe-xslt">
            <xsl:transform version="2.0">
                <xsl:import href="oxf:/oxf/xslt/utils/xbl.xsl"/>
                <xsl:template match="/*">
                    ...
                    <xsl:copy-of select="xxbl:parameter(., 'prefix')"/>
                    <xsl:copy-of select="xxbl:parameter(., 'digits-after-decimal')"/>
                    ...
                </xsl:template>
            </xsl:transform>
        </xbl:template>
    </xbl:binding>
</xbl:xbl>

The arguments of xxbl:parameter() are:
  1. The element corresponding to your component, e.g. the <fr:currency> element written by the user of your component. If your template matches on /*, this will be the current node.
  2. The name of the parameter.
Then in JavaScript, you can access the current value of the property with:

var prefixElement = YAHOO.util.Dom.getElementsByClassName("xbl-fr-currency-prefix", null, this.container)[0];
var prefix = ORBEON.xforms.Document.getValue(prefixElement.id);

Whenever the value of a parameter changes, a method of your JavaScript class is called. The name of this method is parameterFooChanged if "foo" is the name of your property. Parameters names are in lowercase and use dash as a word separator, while the method names use camel case. E.g. if your parameter name is digits-after-decimal, you will defined a method parameterDigitsAfterDecimalChanged.

Sending events from JavaScript

[Since 2012-04-09]

You can dispatch custom events to bindings from JavaScript using the ORBEON.xforms.Document.dispatchEvent() function. If  you are calling it with custom events, make sure you are allowing the custom event names on the binding first:

<xbl:binding xxforms:external-events="acme-super-event acme-famous-event">
    <xbl:handlers>
        <xbl:handler event="acme-super-event" phase="target">
            ...
        </xbl:handler>
    </xbl:handlers
    ...
</xbl:binding>



Aspects of components

[IN PROGRESS]

There are several types of components:
  • components that simply add a little bit of functionality over an existing XForms control (e.g. a simple currency field)
  • components that group together multiple XForms controls. (e.g. a component to enter a date with multiple text fields)
  • components that implement completely new controls (e.g. a map)
  • components that take, and possibly transform, nested markup placed by the user (e.g. a data table with sorting and paging)

Other topics to address

[IN PROGRESS]
  • XPath resolution
  • events used for communication
  • model resolution, including xxf:instance()
  • template copying elements and attributes
  • xxbl:scope in more details
    • what are scopes?
    • xbl:content
    • xxbl:scope=outer, how to override
    • using XSLT
    • id scoping
    • XPath scoping
    • all models are in inner scope
      • might be extended in future: http://wiki.orbeon.com/forms/projects/xforms-model-scoping-rules
    • tip: do not put @id on outer xxforms:group
    • xxbl:scope only on XForms elements!
    • AVTs work based on ancestor scope