IntroductionWhy 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 around the XBL 2 specification to address this need.
See the documentation on already implemented components to get a feel for what is possible. Terminology
Mini-FAQWhat can component do?A lot! Among other things, they can:
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">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:
Why use XBL and not simply XSLT?Using XSLT to implement components is not always a good alternative:
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.
Getting startedCopying an existing componentThe best thing to do is to start looking at existing components:
To create your own component:
Mini-tutorialEncapsulationThe component system favors a strong encapsulation so that components can be:
The default: strong encapsulationBy default, within an<xbl:binding> element, encapsulation is strong: this means that XForms controls, models, and event handlers cannot:
If you place models within <xbl:implementation> or <xbl:template>, the same rule that applies in a top-level XForms document apply:
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 bindingHere 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:
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:
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 LHHA elementsTypical 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 componentYou can find the component discussed in this section in the Orbeon Forms distribution:
<xforms:input> will look like this:<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:
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 stateYou can find the component discussed in this section in the Orbeon Forms distribution:
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:<xbl:implementation> <xforms:model> <xforms:instance> <value/> </xforms:instance> </xforms:model></xbl:implementation>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 instance within the inner group, instead of pointing to the external single-node binding: <xforms:group id="inner-group" xxbl:scope="inner">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="$binding" value="context()"/></xforms:input>What's missing now is to reverse the value: <xforms:input ref="instance()"> <xforms:setvalue ev:event="xforms-value-changed" ref="$binding" value="codepoints-to-string(reverse(string-to-codepoints(context())))"/></xforms: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: <xforms:group xxbl:scope="inner">We use here another nested group bound to the external binding:
<xbl:binding element="fr|tutorial-davinci"> <xbl:implementation> <!-- Local model --> <xforms:model> <xforms:instance> <value/> </xforms:instance> </xforms:model> </xbl:implementation> <xbl:template> <!-- Outer group implementing the single-node binding --> <xforms:group xbl:attr="model context ref bind" xxbl:scope="outer"> <!-- Copy LHHA elements if any --> <xbl:content includes="xforms|label,xforms|help,xforms|hint,xforms|alert"/> <!-- Inner group --> <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> <!-- When the external single-node binding comes to life or its value changes, copy it to the local value after reversing it --> <xforms:group ref="$binding"> <xforms:setvalue ev:event="xforms-enabled xforms-value-changed" ref="instance()" value="codepoints-to-string(reverse(string-to-codepoints($binding)))"/> </xforms:group> <!-- Input points to the internal value --> <xforms:input ref="instance()"> <!-- When the local value changes, copy it to the external single-node binding after reversing it --> <xforms:setvalue ev:event="xforms-value-changed" ref="$binding" value="codepoints-to-string(reverse(string-to-codepoints(context())))"/> </xforms:input> </xforms:group> </xforms:group> </xbl:template></xbl:binding>Components which copy large amounts of user markup[TODO: e.g. tabview, which copies data][TODO: more tutorial-like content
xxbl:scope in more details[TODO:
XForms modelsPlacement of local modelsA 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:
<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 eventsXForms 1.1 specifies the following event sequence upon value changes, insertions, etc.:
XBL components with local models are no different and they receive these events when needed. Construction and destruction of local modelsIt is possible to use components within repeats. These rules apply:
Event handling
Event propagationXBL promotes a strong encapsulation of data and behavior:
[TODO: define "bound node"] [TODO: explain how are xforms-value-changed and other XForms UI event handled] Component user: registering event handlers on the bound nodeEvent handlers can observe elements with XBL bindings:<foo:bar> <xforms:action ev:event="xforms-value-changed"> ... </xforms:action></foo:bar>With XML events, the action handler above observes events going through element <foo:bar>, assuming <foo:bar> has an XML binding. The following is also possible: <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 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 componentThis 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:
[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 componentThe 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:
CSS selectorsThe xbl:includes 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:labelNote the difference of notation in XML/XPath and in CSS to refer to qualified element names:
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"/>In this case, you need to use the ">" combinator, which "describes a childhood relationship between two elements". In this case, the left element must represent the bound element: <xforms:group> <xbl:content includes="fr|link-select1 > xforms|label"/>The CSS selection expression above is exactly equivalent to writing in XPath: descendant-or-self::fr:link-select1/xforms:labelLevel of supportSupported element and attributes from the XBL 2 specification:
Unsupported element and attributes from the XBL 2 specification:
Extensions Generic 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 attributeThe 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:
xxbl:attr="xforms:alert/(@context | @ref | @bind | @model)"xbl:template/xxbl:transform attributeWhen 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>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.
How this works:
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:
NOTE: A future enhancement to this feature might restrict id and XPath scope of global markup to that of the XBL binding. Form Builder extensionsThe Form Builder specific extensions are in thehttp://orbeon.org/oxf/xml/form-builder namespace, often mapped to the fb prefix with xmlns:fb="xmlns="http://orbeon.org/oxf/xml/form-builder".In XBL, each component is defined in an ![]() |


