I want to create a custom XBL component for Orbeon Form Builder that would contain an input text and validate it.
I've managed to create the component and add it to the Form Builder sidebar, but I can't figure out how to do the validation.
The validation I'm looking to do is kind of complex, it's similar to a credit card, some digits have special significance and then there's a checksum that needs to be computed and validated.
What I have so far is this:
<xbl:xbl xmlns:xf="http://www.w3.org/2002/xforms"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:xbl="http://www.w3.org/ns/xbl"
xmlns:xxbl="http://orbeon.org/oxf/xml/xbl"
xmlns:fb="http://orbeon.org/oxf/xml/form-builder"
xmlns:my="http://example.com/xbl">
<xbl:binding element="my|component" id="my-component" xxbl:mode="lhha binding value">
<metadata xmlns="http://orbeon.org/oxf/xml/form-builder">
<display-name lang="en">Component</display-name>
<icon lang="en">
<small-icon>/forms/orbeon/builder/images/input.png</small-icon>
<large-icon>/forms/orbeon/builder/images/input.png</large-icon>
</icon>
<templates>
<view>
<xf:input id="" ref="" xmlns="">
<xf:label ref=""/>
<xf:hint ref=""/>
<xf:help ref=""/>
<xf:alert ref=""/>
</xf:input>
</view>
</templates>
</metadata>
</xbl:binding>
</xbl:xbl>
You can place a validation template in the metadata, at the same level as <view>, but using <bind>. For example:
<bind
type="xf:integer"
constraint="...some XPath expression here..."/>
You could omit the xf:integer type if the value is otherwise validated by the constraint.
With constraint, you should be able to validate your checksum.
If part of your value follows the same rules as credit cards, you could use the standard is-card-number() function as a helper.
Related
I've got the following dialog that I can populate and use if I put it directly into the form. However, I want to put this on all forms without having to put the code into every form. I've saved the dialog into it's own separate email-dialog.xml file and I can call it using the 2nd snippet that is set inside of the properties-local.xml. The dialog shows up as a very tiny box with nothing inside except for the label "EMAIL ADDRESS" at the top where you drag the box. I can't figure out how to get the stuff inside to populate. I've recently updated to the latest version of Orbeon 2020.1.2.2021. Any help or direction would be appreciated.
<xxf:dialog
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:xh="http://www.w3.org/1999/xhtml"
xmlns:xxf="http://orbeon.org/oxf/xml/xforms"
xmlns:xf="http://www.w3.org/2002/xforms"
xmlns:fr="http://orbeon.org/oxf/xml/form-runner"
id="my-dialog-id"
appearance="full"
level="modal"
close="true"
draggable="true"
visible="false">
<xf:label>EMAIL ADDRESS</xf:label>
<xf:input id="textBoxDialog-control" bind="textBoxDialog-bind">
<xf:label ref="$form-resources/textBoxDialog/label"/>
<xf:hint ref="$form-resources/textBoxDialog/hint"/>
<xf:alert ref="$fr-resources/detail/labels/alert"/>
</xf:input>
<xf:trigger id="btnDialogBox-control" bind="btnDialogBox-bind">
<xf:label ref="$form-resources/btnDialogBox/label"/>
<xf:hint ref="$form-resources/btnDialogBox/hint"/>
<xf:alert ref="$fr-resources/detail/labels/alert"/>
<!-- send email function-->
<xf:action event="DOMActivate" type="xpath">
fr:run-process('oxf.fr.detail.process', 'email')
</xf:action>
</xf:trigger>
</xxf:dialog>
<property
as="xs:string"
name="oxf.fr.detail.dialogs.custom.*.*"
value="oxf:/forms/resources/email-dialog.xml"/>
<property as="xs:string" name="oxf.fr.detail.process.send.*.*">
xf:show(dialog="my-dialog-id")
</property>
I assume that you have, in every form, a field named textBoxDialog, which you want to be populated with the value that users enter in the textfield shown inside the dialog you're showing when users click on the Send button. I would suggest that you make two changes to the code of your dialog:
In your xf:input, remove the id, or you might get a duplicate id error since you also have a control with the id textBoxDialog-control in every form.
For the button inside the dialog, don't reference the label of a control btnDialogBox inside the form, but instead puts the label in the code of the dialog.
With those changes, the dialog looks as follows:
And this is the modified version of your code:
<xxf:dialog
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:xh="http://www.w3.org/1999/xhtml"
xmlns:xxf="http://orbeon.org/oxf/xml/xforms"
xmlns:xf="http://www.w3.org/2002/xforms"
xmlns:fr="http://orbeon.org/oxf/xml/form-runner"
id="my-dialog-id"
appearance="full"
level="modal"
close="true"
draggable="true"
visible="false">
<xf:label>EMAIL ADDRESS</xf:label>
<xf:input bind="textBoxDialog-bind">
<xf:label ref="$form-resources/textBoxDialog/label"/>
<xf:hint ref="$form-resources/textBoxDialog/hint"/>
<xf:alert ref="$fr-resources/detail/labels/alert"/>
</xf:input>
<xf:trigger>
<xf:label>Send</xf:label>
<!-- send email function-->
<xf:action event="DOMActivate" type="xpath">
fr:run-process('oxf.fr.detail.process', 'email')
</xf:action>
</xf:trigger>
</xxf:dialog>
I'm trying to create a new component that pretty much works as a pre-configured autocomplete.
The XBL components documentation is very brief, so I adapted as I could and reached this result:
<xbl:xbl xmlns:xf="http://www.w3.org/2002/xforms"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:xbl="http://www.w3.org/ns/xbl"
xmlns:xxbl="http://orbeon.org/oxf/xml/xbl"
xmlns:fb="http://orbeon.org/oxf/xml/form-builder"
xmlns:fr="http://orbeon.org/oxf/xml/form-runner"
xmlns:example="http://example.ro/xbl">
<xbl:binding
id="example-judet"
element="example|judet"
xxbl:mode="lhha binding value">
<metadata xmlns="http://orbeon.org/oxf/xml/form-builder">
<display-name lang="ro">Judeţ</display-name>
<icon lang="ro">
<small-icon>/forms/orbeon/builder/images/listbox.png</small-icon>
<large-icon>/forms/orbeon/builder/images/listbox.png</large-icon>
</icon>
<templates>
<bind required="true()"/>
<view>
<fr:autocomplete
ref="xxf:binding('example-judet')"
labelref="#label"
resource="http://localhost/counties?search={$fr-search-value}">
<xf:label>Judeţ</xf:label>
<xf:hint/>
<xf:help/>
<xf:alert>Câmp obligatoriu</xf:alert>
<xf:itemset ref="item">
<xf:label ref="label"/>
<xf:value ref="value"/>
</xf:itemset>
</fr:autocomplete>
</view>
</templates>
</metadata>
</xbl:binding>
There are 2 problems:
The selector is not working anymore - it's not querying the service on value changes
The alert and help texts work, but they do not show up in the builder's "Control settings" dialog in the appropriate tabs
Any idea what I'm doing wrong?
Is it possible to put a dropdown list in a custom control's Basic Settings? I have tried the below in custom control, but the select doesn't show up. The page on metadata only mentions xf:inputs, but I would to be able to put a select in there (to restrict possible values)
<template>
<ap:binput inputtype="email" min="2">
<xf:label ref=""/>
<xf:hint ref=""/>
<xf:help ref=""/>
<xf:alert ref=""/>
</ap:binput>
</template>
<control-details>
<xf:input ref="#min" type="xf:decimal">
<xf:label lang="en">Minimum Value</xf:label>
<xf:hint />
</xf:input>
<xf:select1 ref="#inputtype">
<xf:label lang="en">Input Type</xf:label>
<xf:hint lang="en">HTML5 Input Type</xf:hint>
<xf:item>
<xf:label lang="en">Third choice</xf:label>
<xf:value>3</xf:value>
</xf:item>
<xf:item>
<xf:label lang="en">Third choice</xf:label>
<xf:value>2</xf:value>
</xf:item>
</xf:select1>
</control-details>
You read the documentation correctly: currently only <xf:input> controls are supported inside <fb:control-details>. Following this questions, I've created an RFE for this.
You can see where this is done if you look at the source of dialog-control-details.xbl, and should you want to support one more control there, it should be reasonably easy to update that file for that purpose.
I've created these two simple files , view.xhtml :
<html xmlns="http://www.w3.org/1999/xhtml"
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:fr="http://orbeon.org/oxf/xml/form-runner"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:xxf="http://orbeon.org/oxf/xml/xforms">
<head>
<title>Test</title>
<xf:model>
<!-- Instance that contains all the books -->
<xf:instance id="people-instance">
<people xmlns="">
<person>
<test/>
</person>
</people>
</xf:instance>
<xf:bind id="test-bind" name="test" ref="person/test"/>
</xf:model>
</head>
<body>
<xf:input id="test-control" bind="test-bind">
<xf:label>THIS</xf:label>
</xf:input>
<fr:my-name test="'test'">
</fr:my-name>
</body>
And my-name.xbl :
<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"
xmlns:fb="http://orbeon.org/oxf/xml/form-builder">
<xbl:binding element="fr|my-name" id="fr-my-name" xxbl:mode="lhha binding value">
<metadata xmlns="http://orbeon.org/oxf/xml/form-builder" xmlns:xf="http://www.w3.org/2002/xforms">
<templates>
<view>
<fr:my-name id="" appearance="minimal" xmlns="" test="" >
</fr:my-name>
</view>
</templates>
<xf:input ref="#test">
<xf:label lang="en">Resource URI</xf:label>
</xf:input>
</metadata>
<xbl:template>
<!-- Local model and instance -->
<xf:var name="test-avt" xbl:attr="xbl:text=test" xxbl:scope="outer"/>
<xf:var name="test" xbl:attr="xbl:text=test" >
<xf:action ev:event="xforms-enabled xforms-value-changed">
<xf:setvalue ref="instance('test')" value="$test"/>
</xf:action>
<xxf:sequence value="xxf:evaluate-avt($test-avt)" xxbl:scope="outer"/>
</xf:var>
<xf:model>
<xf:instance id="test"><value/></xf:instance>
<xf:instance><value/></xf:instance>
<xf:submission id="save-submission" ref="instance('test')"
action="/exist/rest/db/orbeon/my-name/person.xml"
method="put" replace="none">
<xf:message ev:event="xforms-submit-error" level="modal">An error occurred while saving!</xf:message>
</xf:submission>
</xf:model>
<xf:input class="xforms-disabled" ref="xxf:instance('people-instance')//*[name() = saxon:evaluate($test)]" >
<xf:label>Your name</xf:label>
<xf:send ev:event="xforms-value-changed" submission="save-submission"/>
</xf:input>
</xbl:template>
</xbl:binding>
Now I would like pass parameters from my view.xhtml to XBL so when user inserts something in xf:input id='test-control' it would cause that the text will be saved in database.
The problem is, instead of what user actually inserted in my database I have :
<value>'test'</value>
instead of expected, f.e. if I insert 123
<value>123</value>
Anyone has any idea why my code doesn't work and how to fix this ?
Here is an example which works. I put both files together to make it easier to try.
A few things I changed:
If your test attribute is in fact an AVT, it shouldn't have quotes around it. So you have for example test="foo{42}" which evaluates to foo42, which, if I understand well, is the name of the XML element you want to observe.
You probably don't want saxon:evaluate($test): the AVT is already evaluated and the result available in the variable $test. There is no point evaluating the result as an XPath expression again.
The 2nd var must not have xbl:attr="xbl:text=test"
Optional: I used xxf:binding-context('fr-my-name')/root() instead of xxf:instance(), but xxf:instance() will work too (although its usage is not recommended).
Optional: I moved the XBL model under xbl:implementation. It makes it clearer that this is not dependent on the template.
Here is my custom control:
<?xml version="1.0" encoding="UTF-8"?>
<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:xi="http://www.w3.org/2001/XInclude"
xmlns:xxi="http://orbeon.org/oxf/xml/xinclude"
xmlns:xxforms="http://orbeon.org/oxf/xml/xforms"
xmlns:fr="http://orbeon.org/oxf/xml/form-runner"
xmlns:saxon="http://saxon.sf.net/"
xmlns:xbl="http://www.w3.org/ns/xbl">
<metadata xmlns="http://orbeon.org/oxf/xml/form-builder">
<display-name lang="en">Custom Controls</display-name>
</metadata>
<xbl:binding id="fb-input-country-selector" element="xforms|country-selector">
<metadata xmlns="http://orbeon.org/oxf/xml/form-builder">
<display-name lang="en">Country Selector</display-name>
<icon lang="en">
<small-icon>/forms/orbeon/builder/images/dropdown.png</small-icon>
<large-icon>/forms/orbeon/builder/images/dropdown.png</large-icon>
</icon>
<datatype>xforms:string</datatype>
<template>
<xforms:select1 id="" appearance="minimal" ref="" xmlns="">
<xforms:label ref=""/>
<xforms:hint ref=""/>
<xforms:help ref=""/>
<xforms:alert ref="$fr-resources/detail/labels/alert"/>
<xforms:item>
<xforms:label>[Select...]</xforms:label>
<xforms:value/>
</xforms:item>
<xforms:itemset nodeset="doc('oxf:/apps/xforms-controls/services/countries.xml')/countries/country">
<xforms:label ref="name"/>
<xforms:value ref="us-code"/>
</xforms:itemset>
</xforms:select1>
</template>
</metadata>
</xbl:binding>
</xbl:xbl>
The problem is that the data items are not grabbed from the XML, but if I introduce the template itself in the form structure, it works.
Anyone have any ideea?
Creating XBL components is tricky. At least the first time you do it. A few things:
To avoid confusions, I recommend you define components in your own namespace. In the example below, I used xmlns:my="http://www.example.com/".
The content of fb:template is how using the component looks like, not its implementation. So, it should look like:
<fb:template>
<my:country-selector>
<xforms:label ref=""/>
<xforms:hint ref=""/>
<xforms:help ref=""/>
<xforms:alert ref=""/>
</my:country-selector>
</fb:template>
The implementation is inside xbl:template.
So the component can be found at runtime, you need to place it in a directory xbl/my/country-selector/country-selector.xbl. The my part of the directory is mapped to the namespace by you adding the following property in your properties-local.xml:
<property as="xs:string" name="oxf.xforms.xbl.mapping.my"
value="http://www.example.com/"/>
(Often, we use the same name for the directory and the prefix, but they don't need to be the same. E.g. the fr:* components are in a directory called orbeon.)
So Form Builder shows your component in the sidebar, you need to add a property like:
<property as="xs:string" name="oxf.fb.toolbox.group.other.uri.*.*"
value="oxf:/xbl/my/country-selector/country-selector.xbl"/>
And here is the full source for country-selector.xbl:
<xbl:xbl xmlns:xh="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:xi="http://www.w3.org/2001/XInclude"
xmlns:xxi="http://orbeon.org/oxf/xml/xinclude"
xmlns:xxf="http://orbeon.org/oxf/xml/xforms"
xmlns:my="http://www.example.com/"
xmlns:saxon="http://saxon.sf.net/"
xmlns:fb="http://orbeon.org/oxf/xml/form-builder"
xmlns:xbl="http://www.w3.org/ns/xbl"
xmlns:xxbl="http://orbeon.org/oxf/xml/xbl">
<metadata xmlns="http://orbeon.org/oxf/xml/form-builder">
<display-name lang="en">Custom Controls</display-name>
</metadata>
<xbl:binding id="my-country-selector" element="my|country-selector" xxbl:mode="lhha binding value">
<fb:metadata>
<fb:display-name lang="en">Country Selector</fb:display-name>
<fb:icon lang="en">
<fb:small-icon>/forms/orbeon/builder/images/dropdown.png</fb:small-icon>
<fb:large-icon>/forms/orbeon/builder/images/dropdown.png</fb:large-icon>
</fb:icon>
<fb:datatype>xforms:string</fb:datatype>
<fb:template>
<my:country-selector>
<xforms:label ref=""/>
<xforms:hint ref=""/>
<xforms:help ref=""/>
<xforms:alert ref=""/>
</my:country-selector>
</fb:template>
</fb:metadata>
<xbl:template>
<xforms:select1 appearance="minimal" ref="xxf:binding('my-country-selector')">
<xforms:item>
<xforms:label>[Select...]</xforms:label>
<xforms:value/>
</xforms:item>
<xforms:itemset nodeset="doc('oxf:/forms/orbeon/controls/service/countries.xml')/countries/country">
<xforms:label ref="name"/>
<xforms:value ref="us-code"/>
</xforms:itemset>
</xforms:select1>
</xbl:template>
</xbl:binding>
</xbl:xbl>