将设置添加到自定义控件的“基本设置”对话框

Add settings to the Basic Settings dialog for a custom control

希望 Orbeon 菜鸟问题不要太多。我已经为 Orbeon 构建了一个自定义控件(目前是一个简单的滑块),并希望添加更改“基本设置”对话框中范围输入的最小值、最大值和步长参数的功能。我查看了 Dynamic Driven Dropdown 并在控件元数据中添加了控件详细信息部分,但我对如何让它们显示以及如何在实际输入元素上使用值感到困惑。任何帮助/示例代码将不胜感激。

<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:xi="http://www.w3.org/2001/XInclude"
        xmlns:xxi="http://orbeon.org/oxf/xml/xinclude"
        xmlns:xxf="http://orbeon.org/oxf/xml/xforms"
        xmlns:fr="http://orbeon.org/oxf/xml/form-runner"
        xmlns:saxon="http://saxon.sf.net/"
        xmlns:oxf="http://www.orbeon.com/oxf/processors"
        xmlns:xbl="http://www.w3.org/ns/xbl"
        xmlns:xxbl="http://orbeon.org/oxf/xml/xbl"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xbl:binding id="fr-slider" element="fr|slider"
           xxf:external-events="fr-value-changed"
           xxbl:mode="lhha binding value">
        <metadata xmlns="http://orbeon.org/oxf/xml/form-builder">
            <display-name lang="en">Slider</display-name>
            <icon lang="en">
                <small-icon>/forms/orbeon/builder/images/timeline_marker.png</small-icon>
                <large-icon>/forms/orbeon/builder/images/timeline_marker.png</large-icon>
            </icon>
            <datatype>xf:number</datatype>
            <template>
                <fr:slider>
                    <xf:label ref=""/>
                    <xf:hint ref=""/>
                    <xf:help ref=""/>
                    <xf:alert ref=""/>
                    <xf:min ref=""/>
                    <xf:max ref=""/>
                    <xf:step ref=""/>
                </fr:slider>
            </template>
            <control-details>
            <xf:input ref="xf:min/@ref" type="number">
                <xf:label>Minimum Value</xf:label>
                <xf:hint />
            </xf:input>
            <xf:input ref="xf:max/@ref" type="number">
                <xf:label>Maximum Value</xf:label>
                <xf:hint />
            </xf:input>
            <xf:input ref="xf:step/@ref" type="number">
                <xf:label>Step Size</xf:label>
                <xf:hint>Smallest change in value the slider will allow</xf:hint>
            </xf:input>
            </control-details>
        </metadata>
        <xbl:resource>
            <xbl:style>
                input.fr-slider { width: 100% };
            </xbl:style>
        </xbl:resource>
        <xbl:template xxbl:transform="oxf:unsafe-xslt">
            <xsl:transform version="2.0">
            <xsl:import href="oxf:/oxf/xslt/utils/xbl.xsl"/>
            <xsl:template match="/*">
                <xh:input type="range" min="0" max="10" step="1" class="fr-slider"/>
            </xsl:template>
            </xsl:transform>
        </xbl:template>
    </xbl:binding>
</xbl:xbl>

要让 <control-detail> 中的输入显示在基本设置对话框中,您需要将 lang 参数添加到 <xf:label><xf:hint> 元素(感谢 @avernet解决这个问题)。

下面是使用 xhtml 输入元素作为输入的自定义控件 cust:betterinput 的完整示例。它使用自定义设置(以及数据类型 [在控制设置 -> 验证和警报 -> 数据类型中设置])来配置 html 元素上的参数(请注意,自定义参数已更改为控制参数) . Javascript 用于同步 xhtml 输入和 xforms 输入之间的值(以便 Orbeon 捕获该值)。该示例基于 Orbeon

中包含的 number control

xbl/cust/betterinput/betterinput.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:xi="http://www.w3.org/2001/XInclude"
        xmlns:xxi="http://orbeon.org/oxf/xml/xinclude"
        xmlns:xxf="http://orbeon.org/oxf/xml/xforms"
        xmlns:fr="http://orbeon.org/oxf/xml/form-runner"
        xmlns:saxon="http://saxon.sf.net/"
    xmlns:exf="http://www.exforms.org/exf/1-0"
        xmlns:oxf="http://www.orbeon.com/oxf/processors"
        xmlns:xbl="http://www.w3.org/ns/xbl"
        xmlns:xxbl="http://orbeon.org/oxf/xml/xbl"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:cust="http://www.cust.com/">

  <xbl:script src="/xbl/cust/betterinput/betterinput.js"/>
    <xbl:binding
          id="cust-betterinput"
            element="cust|betterinput"
            xxbl:mode="lhha binding value focus"
            xxbl:label-for="html-input">
        <metadata xmlns="http://orbeon.org/oxf/xml/form-builder">
            <display-name lang="en">Better Input</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>
            <description lang="en"/>
            <templates>
                <view>
                    <cust:betterinput type="" prefix="" suffix="">
                        <xf:label ref=""/>
                        <xf:hint ref=""/>
                        <xf:help ref=""/>
                        <xf:alert ref=""/>
                    </cust:betterinput>
                </view>
            </templates>
            <control-details>
                <xf:input ref="@type">
                    <xf:label lang="en">Input Type</xf:label>
                    <xf:hint lang="en">HTML5 Input Type</xf:hint>
                </xf:input>
                <xf:input ref="@prefix">
                    <xf:label lang="en">Input Prefix</xf:label>
                    <xf:hint/>
                </xf:input>
                <xf:input ref="@suffix">
                    <xf:label lang="en">Input Suffix</xf:label>
                    <xf:hint/>
                </xf:input>
            </control-details>
        </metadata>
    <xbl:resources>
      <xbl:style src="/xbl/cust/betterinput/betterinput.css"/>
    </xbl:resources>
        <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:variable
              name="js-object"
              as="xs:string"
              select="'YAHOO.xbl.cust.BetterInput.instance(this)'"/>
                    <xf:group>
            <xf:action type="javascript" ev:event="xforms-disabled" ev:target="#observer">
                            <xsl:value-of select="$js-object"/>.destroy();
                        </xf:action>
                        <xf:var name="binding" value="xxf:binding('cust-betterinput')"/>
                        <xf:var name="view"
                                value="exf:readonly($binding) and property('xxf:readonly-appearance') = 'static'"/>
            <xf:action
                ev:target="#observer"
                ev:event="xforms-enabled xforms-value-changed">
                            <xxf:script>
                                <xsl:value-of select="$js-object"/>.updateWithServerValue();
                            </xxf:script>
                        </xf:action>
            <xf:action
                ev:target="#observer"
                ev:event="DOMFocusOut">
                            <xxf:script>
                                <xsl:value-of select="$js-object"/>.updateWithServerValue();
                            </xxf:script>
                        </xf:action>
                        <xf:var name="htmlinputs" value="' email url color number datetime-local '" />
                        <xsl:copy-of select="xxbl:parameter(., 'type')"/>
            <xsl:copy-of select="xxbl:parameter(., 'prefix')"/>
            <xsl:copy-of select="xxbl:parameter(., 'suffix')"/>
            <xf:group ref="$binding[not($view)]">
                            <xf:input ref="." class="betterinput-xform-input xforms-hidden">
                <xf:action type="javascript" id="xf-ro" ev:event="xforms-readonly"><xsl:value-of select="$js-object"/>.readonly();</xf:action>
                <xf:action type="javascript" id="xf-rw" ev:event="xforms-readwrite"><xsl:value-of select="$js-object"/>.readwrite();</xf:action>
                            </xf:input>
                <xh:span class="{{(if ($prefix) then 'input-prepend' else (), if ($suffix) then 'input-append' else ())}}">
                    <xf:group class="add-on" ref=".[$prefix]"><xf:output value="$prefix"/></xf:group>
                                        <xh:input  id="html-input" class="betterinput-html-input" type="{{
                                            if (not(type = '') and contains($htmlinputs, concat(' ', $type, ' '))) then
                                                $type
                                            else
                                                if (contains(' decimal integer double ', concat(' ', xxf:type($binding), ' '))) then
                                                    'number'
                                                else
                                                    if (contains($htmlinputs, concat(' ', xxf:type($binding), ' '))) then
                                                        xxf:type($binding)
                                                    else
                                                    'text'
                                        }}"/>
                    <xf:group class="add-on" ref=".[$suffix]"><xf:output value="$suffix"/></xf:group>
                </xh:span>
                        </xf:group>
            <!-- Static readonly mode -->
            <xf:group ref="$binding[$view]" class="{{(if ($prefix) then 'input-prepend' else (), if ($suffix) then 'input-append' else ())}}">
                <xf:group class="add-on" ref=".[$prefix]"><xf:output value="$prefix"/></xf:group>
                <xf:input ref="$binding[$view]" class="betterinput-html-input" />
                <xf:group class="add-on" ref=".[$suffix]"><xf:output value="$suffix"/></xf:group>
                        </xf:group>
                    </xf:group>
                </xsl:template>
            </xsl:transform>
        </xbl:template>
    </xbl:binding>
</xbl:xbl>

xbl/cust/betterinput/betterinput.js

(function() {

    var $ = ORBEON.jQuery;
    var AS = ORBEON.xforms.server.AjaxServer;
    var Document = ORBEON.xforms.Document;

    YAHOO.namespace("xbl.cust");
    YAHOO.xbl.cust.BetterInput = function() {};
    ORBEON.xforms.XBL.declareClass(YAHOO.xbl.cust.BetterInput, "xbl-cust-betterinput");
    YAHOO.xbl.cust.BetterInput.prototype = {

        xformsInputElement: null,
        visibleInputElement: null,

        prefixElement: null,
        prefix: null,

        init: function() {
            // Get information from the DOM
            console.log('betterinput init');

            this.xformsInputElement = YAHOO.util.Dom.getElementsByClassName("betterinput-xform-input", null, this.container)[0];
            this.visibleInputElement = YAHOO.util.Dom.getElementsByClassName("betterinput-html-input", null, this.container)[0];

            // Properties
            // Find prefix based on class/control name, as this JS can be used with fr:number and fr:currency and properties use the control name
            var controlClassPrefix = null;
            var containerClasses = this.container.className.split(" ");
            for (var classIndex = 0; classIndex < containerClasses.length; classIndex++) {
                var currentClass = containerClasses[classIndex];
                if (currentClass.indexOf("xbl-cust-") == 0) {
                    controlClassPrefix = currentClass;
                    break;
                }
            }

            this.prefixElement            = YAHOO.util.Dom.getElementsByClassName(controlClassPrefix + "-prefix", null, this.container)[0];
            this.prefix                   = Document.getValue(this.prefixElement.id);

            // Register listeners

            // Restore input type, send the value to the server, and updates value after server response
            $(this.visibleInputElement).on('change blur', _.bind(function(e) {
                this.sendValueToServer();
                var formId = $(this.container).parents('form').attr('id');

                // Always update visible value with XForms value
                // - relying just value change event from server is not enough
                // - value change not dispatched if server value hasn't changed
                // - if visible changed, but XForms hasn't, we still need to show XForms value
                // - see: https://github.com/orbeon/orbeon-forms/issues/1026
                AS.nextAjaxResponse(formId).then(_.bind(this.updateWithServerValue, this));
            }, this));

            $(this.visibleInputElement).on('keypress', _.bind(function(e) {
                if (e.which == 13)
                    this.sendValueToServer();
            }, this));
        },

        setFocus: function() {
            this.visibleInputElement.focus();
         },

        sendValueToServer: function() {
            var newValue = this.visibleInputElement.value;
            Document.setValue(this.xformsInputElement.id, newValue);
        },

        updateWithServerValue: function() {
            var value = Document.getValue(this.xformsInputElement.id);

            this.visibleInputElement.value = value;

            // Also update disabled because this might be called upon an iteration being moved, in which case all the control properties must be updated
            this.visibleInputElement.disabled = YAHOO.util.Dom.hasClass(this.xformsInputElement, "xforms-readonly");
        },

        readonly: function() {
            this.visibleInputElement.disabled = true;
        },

        readwrite: function() {
            this.visibleInputElement.disabled = false;
        },
    };
})();