【问题标题】:JSF 2 -- Composite component with optional listener attribute on f:ajaxJSF 2——在 f:ajax 上具有可选监听器属性的复合组件
【发布时间】:2012-07-07 06:31:37
【问题描述】:

我有一个看起来像这样的复合组件:

<!DOCTYPE html>
<html 
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:dm="http://davemaple.com/dm-taglib"
    xmlns:rich="http://richfaces.org/rich"
    xmlns:cc="http://java.sun.com/jsf/composite"
    xmlns:fn="http://java.sun.com/jsp/jstl/functions"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:a4j="http://richfaces.org/a4j">

<cc:interface>
    <cc:attribute name="styleClass" />
    <cc:attribute name="textBoxStyleClass" />
    <cc:attribute name="inputTextId" />
    <cc:attribute name="labelText" />
    <cc:attribute name="tabindex" />
    <cc:attribute name="required" default="false" />
    <cc:attribute name="requiredMessage" />
    <cc:attribute name="validatorId" />
    <cc:attribute name="converterId" />
    <cc:attribute name="title"/>
    <cc:attribute name="style"/>
    <cc:attribute name="unicodeSupport" default="false"/>
    <cc:attribute name="tooltip" default="false"/>
    <cc:attribute name="tooltipText" default=""/>    
    <cc:attribute name="tooltipText" default=""/>
    <cc:attribute name="onfail" default=""/>
    <cc:attribute name="onpass" default=""/>
</cc:interface>

<cc:implementation>

        <ui:param name="converterId" value="#{! empty cc.attrs.converterId ? cc.attrs.converterId : 'universalConverter'}" />
        <ui:param name="validatorId" value="#{! empty cc.attrs.validatorId ? cc.attrs.validatorId : 'universalValidator'}" />
        <ui:param name="component" value="#{formFieldBean.getComponent(cc.attrs.inputTextId)}" />
        <ui:param name="componentValid" value="#{((facesContext.maximumSeverity == null and empty component.valid) or component.valid) ? true : false}" />
        <ui:param name="requiredMessage" value="#{! empty cc.attrs.requiredMessage ? cc.attrs.requiredMessage : msg['validation.generic.requiredMessage']}" />
        <ui:param name="clientIdEscaped" value="#{fn:replace(cc.clientId, ':', '\\\\\\\\:')}" />

        <h:panelGroup layout="block" id="#{cc.attrs.inputTextId}ValidPanel" style="display:none;">
            <input type="hidden" id="#{cc.attrs.inputTextId}Valid" value="#{componentValid}" />
        </h:panelGroup>

        <dm:outputLabel for="#{cc.clientId}:#{cc.attrs.inputTextId}" id="#{cc.attrs.inputTextId}Label">#{cc.attrs.labelText}</dm:outputLabel>
        <dm:inputText 
            styleClass="#{cc.attrs.textBoxStyleClass}"
               tabindex="#{cc.attrs.tabindex}"
               id="#{cc.attrs.inputTextId}"
               required="#{cc.attrs.required}"
               requiredMessage="#{requiredMessage}"
               title="#{cc.attrs.title}"
               unicodeSupport="#{cc.attrs.unicodeSupport}">
            <f:validator validatorId="#{validatorId}" />
            <f:converter converterId="#{converterId}" />
            <cc:insertChildren />
            <f:ajax 
                event="blur" 
                execute="@this" 
                render="#{cc.attrs.inputTextId}ValidPanel #{cc.attrs.inputTextId}Msg" 
                onevent="on#{cc.attrs.inputTextId}Event" />
        </dm:inputText>
        <rich:message for="#{cc.clientId}:#{cc.attrs.inputTextId}" id="#{cc.attrs.inputTextId}Msg" style="display: none;" />
        <script>
            function on#{cc.attrs.inputTextId}Event(e) {
                if(e.status == 'success') {
                    $('##{clientIdEscaped}\\:#{cc.attrs.inputTextId}').trigger($('##{cc.attrs.inputTextId}Valid').val()=='true'?'pass':'fail');
                }
            }
            $('##{clientIdEscaped}\\:#{cc.attrs.inputTextId}').bind('fail', function() {
                $('##{clientIdEscaped}\\:#{cc.attrs.inputTextId}, ##{clientIdEscaped}\\:#{cc.attrs.inputTextId}Label, ##{cc.attrs.inputTextId}Msg, ##{cc.id}Msg').addClass('error');
                $('##{cc.id}Msg').html($('##{clientIdEscaped}\\:#{cc.attrs.inputTextId}Msg').html());
                #{cc.attrs.onfail}
            }).bind('pass', function() {
                $('##{clientIdEscaped}\\:#{cc.attrs.inputTextId}, ##{clientIdEscaped}\\:#{cc.attrs.inputTextId}Label, ##{cc.attrs.inputTextId}Msg, ##{cc.id}Msg').removeClass('error');
                $('##{cc.id}Msg').html($('##{clientIdEscaped}\\:#{cc.attrs.inputTextId}Msg').html());
                #{cc.attrs.onpass}
            });
        </script>
        <a4j:region rendered="#{facesContext.maximumSeverity != null and !componentValid}">
            <script>
                $(document).ready(function() { 
                    $('##{clientIdEscaped}\\:#{cc.attrs.inputTextId}').trigger('fail');
                });
            </script>
        </a4j:region>
</cc:implementation>

</html>

我希望能够添加一个可选的“侦听器”属性,如果定义了该属性,它将向我的 f:ajax 添加一个事件侦听器,但我无法弄清楚如何实现这一点。任何帮助将不胜感激。

【问题讨论】:

    标签: ajax jsf jsf-2 listener composite-component


    【解决方案1】:

    不幸的是,以上所有方法都对我不起作用,所以我摆弄了一下,想出了以下解决方案。

    <cc:interface type="someComponent">
        <cc:attribute name="listener" method-signature="void listener()" />
    </cc:interface>
    <cc:implementation>
        <h:someComponent>
            <p:ajax listener="#{cc.attrs.listener}" onstart="#{cc.attrs.containsKey('listener') ? '' : 'return false'}" />
        </h:someComponent>
    </cc:implementation>
    

    这将始终绑定 ajax 行为,但只有在实际存在侦听器时才执行它。

    【讨论】:

      【解决方案2】:

      我也有同样的问题,我的解决方案是为 action 方法创建默认值。我只需要创建一个类:MyComponent.java,其中包含所有默认方法签名。

      <cc:interface>
          <cc:attribute name="listener" method-signature="void listener()"
                        default="#{myComponent.doNothing()}" />
      </cc:interface>
      <cc:implementation>
          <h:someComponent>
              <f:ajax listener="#{cc.attrs.listener}" />
          </h:someComponent>
      </cc:implementation>
      

      【讨论】:

        【解决方案3】:

        您需要指定&lt;cc:attribute&gt; 标记的method-signature 属性,以便将属性值视为方法表达式。您可以使用 JSTL 视图构建时间标记 &lt;c:if&gt; 有条件地添加 &lt;f:ajax&gt; 标记。

        <cc:interface>
            <cc:attribute name="listener" method-signature="void listener()" />
        </cc:interface>
        <cc:implementation>
            <h:someComponent>
                <c:if test="#{cc.getValueExpression('listener') != null}">
                    <f:ajax listener="#{cc.attrs.listener}" />
                </c:if>
            </h:someComponent>
        </cc:implementation>
        

        #{not empty cc.attrs.listener} 不起作用,因为 EL 会隐式地将属性评估为值表达式)

        那么你可以如下使用它:

        <my:someComposite listener="#{bean.listener}" />
        

        或者当您的环境不支持 EL 2.2 时,则创建一个支持组件:

        @FacesComponent("someComponent")
        public class SomeComponent extends UINamingContainer {
        
            public boolean isHasListener() {
                return getValueExpression("listener") != null;
            }
        
        }
        

        要声明并用作

        <cc:interface type="someComponent">
            <cc:attribute name="listener" method-signature="void listener()" />
        </cc:interface>
        <cc:implementation>
            <h:someComponent>
                <c:if test="#{cc.hasListener}">
                    <f:ajax listener="#{cc.attrs.listener}" />
                </c:if>
            </h:someComponent>
        </cc:implementation>
        

        【讨论】:

        • 好的。我已经做到了这一点,但我希望它是一个可选属性。当组件的客户端不提供它时,我不希望 f:ajax 使用它。有没有办法让它在 f:ajax 上成为可选的?
        • 嗯,这样不行。 &lt;c:if&gt; 尝试将其解析为值表达式:/ 抱歉,我将不得不回复我的答案。
        • 好的。选项 2 没有漂亮的界面,但我想我会选择它,这样我就可以隐藏其他 f:ajax 功能。
        • 理论上,当使用“支持组件”(&lt;cc:interface componentType&gt;)时应该是可行的,它有一个 getter 方法来检查是否定义了“listener”属性,以便您可以使用&lt;c:if test="#{cc.hasListener}"&gt;或者。没有办法告诉 EL 不将其解析为值表达式。由于#{cc.attrs} 的EL 解析器,即使#{not cc.attrs.containsKey('listener')} 也无法工作。我已经更新了答案。
        • 更好的是,#{not empty cc.getValueExpression('listener')} 为我工作。很抱歉这么多更新,这对我来说也是第一次:)
        猜你喜欢
        • 2015-01-02
        • 1970-01-01
        • 2012-08-26
        • 2013-06-14
        • 2011-07-28
        • 2011-11-28
        • 2012-03-19
        • 1970-01-01
        • 2013-05-05
        相关资源
        最近更新 更多