JSF 2 - 如何将 Ajax 侦听器方法添加到复合组件接口?

Posted

技术标签:

【中文标题】JSF 2 - 如何将 Ajax 侦听器方法添加到复合组件接口?【英文标题】:JSF 2 - How can I add an Ajax listener method to composite component interface? 【发布时间】:2011-09-21 04:23:22 【问题描述】:

我有一个 JSF 2 复合组件,它采用了一些 Ajax 行为。我想在复合组件内的<f:ajax> 标签中添加listener 方法,但listener 方法应作为<composite:attribute><composite:interface> 中提供。

我的复合组件中的 <f:ajax> 标记目前被硬编码为这样的侦听器:

<f:ajax
    event="valueChange"
    execute="@this"
    listener="#controller.genericAjaxEventLogger"
    render="#cc.attrs.ajaxRenderTargets" />

bean 上的监听器方法有这个签名:

public void genericAjaxEventLogger(AjaxBehaviorEvent event) 
        throws AbortProcessingException 
    // implementation code...

我希望复合组件是这样的,以便页面可以提供自己的事件方法,但我无法确定接口的正确语法。

<f:ajax
    event="valueChange"
    execute="@this"
    listener="#cc.attrs.ajaxEventListener"
    render="#cc.attrs.ajaxRenderTargets" />

我该怎么做?

已更新解决方案:

我采用了 BalusC 建议的方法,效果很好。相关的sn-ps是:

复合组件中的接口声明

<composite:interface>
    <composite:attribute
        name="myattributeUpdatedEventListener"
        method-signature="void listener()"
        required="true" />
    ...
</composite:interface>

我的复合组件中使用的 Ajax 标记

<f:ajax
    event="valueChange"
    execute="@this"
    listener="#cc.attrs.myattributeUpdatedEventListener"
    render="#cc.attrs.ajaxRenderTargets" />

页面中我使用复合组件的地方

<h:form>
    <compcomp:myCompositeComponent
        myattributeUpdatedEventListener="#myBackingBean.updatedEventListenerXYZ" />
</h:form>

还有我的backing bean上的方法

public void updatedEventListenerXYZ() 
    // do something here...

【问题讨论】:

感谢您的解决方案,它对我很有帮助。 是否可以通过带参数的方法来做到这一点?我的意思是,不要像示例中那样传递 #myBackingBean.updatedEventListenerXYZ ,而是传递类似 #myBackingBean.myMethod(cc.attrs.myparam) 的东西 【参考方案1】:

如果你可以去掉AjaxBehaviorEvent参数,

public void genericAjaxEventLogger() 
    // ...

那么你可以使用

<cc:attribute name="ajaxEventListener" method-signature="void listener()" />

如果参数是强制性的(用于记录?),那么您需要重新指定属性,如下所示

<cc:attribute name="ajaxEventListener" method-signature="void listener(javax.faces.event.AjaxBehaviorEvent)" />

但是,这在此处无法正常工作

<f:ajax listener="#cc.attrs.ajaxEventListener" />

关于 GF 3.1 + Mojarra 2.1.1:

SEVERE: javax.faces.FacesException: wrong number of arguments
    at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:89)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:409)
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1534)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:281)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:98)
    at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:91)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:162)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:326)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:227)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:170)
    at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:822)
    at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:719)
    at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1013)
    at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:225)
    at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
    at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
    at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
    at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
    at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
    at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
    at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.IllegalArgumentException: wrong number of arguments
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at com.sun.el.parser.AstValue.invoke(AstValue.java:234)
    at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:297)
    at com.sun.faces.facelets.el.ContextualCompositeMethodExpression.invoke(ContextualCompositeMethodExpression.java:177)
    at com.sun.faces.facelets.tag.TagAttributeImpl$AttributeLookupMethodExpression.invoke(TagAttributeImpl.java:450)
    at com.sun.faces.facelets.tag.jsf.core.AjaxBehaviorListenerImpl.processAjaxBehavior(AjaxHandler.java:447)
    at javax.faces.event.AjaxBehaviorEvent.processListener(AjaxBehaviorEvent.java:113)
    at javax.faces.component.behavior.BehaviorBase.broadcast(BehaviorBase.java:102)
    at javax.faces.component.UIComponentBase.broadcast(UIComponentBase.java:760)
    at javax.faces.component.UICommand.broadcast(UICommand.java:300)
    at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:794)
    at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1259)
    at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
    ... 28 more

我不确定这是否是错误。为此,我需要投入更多时间来确定它。但是,可以通过创建一个支持组件来解决此问题,该组件从属性中获取MethodExpression 并使用正确数量的参数进行调用。这是一个完整的启动示例:

<ui:component 
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:cc="http://java.sun.com/jsf/composite"
    xmlns:ui="http://java.sun.com/jsf/facelets"
>
    <cc:interface componentType="testCC">
        <cc:attribute name="ajaxEventListener" method-signature="void listener(javax.faces.event.AjaxBehaviorEvent)" />
    </cc:interface>
    <cc:implementation>
        <h:commandButton value="Submit">
            <f:ajax listener="#cc.ajaxEventListener" />
        </h:commandButton>
    </cc:implementation>
</ui:component>

package com.example;

import javax.el.MethodExpression;
import javax.faces.component.FacesComponent;
import javax.faces.component.UINamingContainer;
import javax.faces.context.FacesContext;
import javax.faces.event.AjaxBehaviorEvent;

@FacesComponent(value="testCC")
public class TestCC extends UINamingContainer 

    public void ajaxEventListener(AjaxBehaviorEvent event) 
        FacesContext context = FacesContext.getCurrentInstance();
        MethodExpression ajaxEventListener = (MethodExpression) getAttributes().get("ajaxEventListener");
        ajaxEventListener.invoke(context.getELContext(), new Object[]  event );
    


无论如何,我相信支持组件为实现您心中所想的功能需求的新方法打开了大门;)

【讨论】:

谢谢。我发现了很多在 CC 接口中声明动作方法的例子,但没有一个声明监听器方法的例子。 method-signaturelistener() 部分是 JSF 可以理解的特殊值吗? 方法名不相关。您甚至可以将其命名为xyz()。这只是一个自我描述的例子。最重要的是方法返回和参数类型的全限定类名。 正是我想要的 ;) @BalusC 你找到相关的问题了吗?我仍然有 Mojarra 2.1.27 + Tomcat 6 的问题(对于两个听众,一个有论据,另一个没有)。您认为为此打开 Jira 问题值得吗? @BalusC 当我使用这个构造时,我也遇到了一个异常。但我正在尝试使用&lt;f:actionListener binding="#cc.attrs.mymethod"&gt;&lt;/f:actionListner&gt; 而不是ajax。它抱怨说“无法将 org.apache.el.MethodExpressionImpl 类型的 org.apache.el.MethodExpressionImpl@8819d678 转换为接口 javax.faces.event.ActionListener”【参考方案2】:

这是一个关于如何使用接口设置属性并在实现中引用它的示例。您必须定义将被调用的方法的方法签名。这会通知复合组件处理程序在 #cc.attrs.ajaxEventListener 表达式中包含一个方法值,而不是值表达式。

<cc:interface name="composite-comp"
    <cc:attribute required="true" name="ajaxEventListener" 
                  method-signature="void f1(javax.faces.event.AjaxBehaviorEvent)" />
    <cc:attribute required="true" name="ajaxRenderTargets" />
</cc:interface>

<cc:implementation>
    .
    .
    .
    <f:ajax event="valueChange" execute="@this"
        listener="#cc.attrs.ajaxEventListener"
        render="#cc.attrs.ajaxRenderTargets" />
</cc:implementation>

【讨论】:

你是在什么环境下测试/使用的?这对我来说在 GF 3.1 和带有 Mojarra 2.1.1 的 Tomcat 7 上失败了。

以上是关于JSF 2 - 如何将 Ajax 侦听器方法添加到复合组件接口?的主要内容,如果未能解决你的问题,请参考以下文章

如何将JavaScript侦听器添加到PrimeFaces Ajax事件

JSF 2——在 f:ajax 上具有可选监听器属性的复合组件

JSF2 <h:selectOneMenu 和 <f:ajax 侦听器在 PrettyFaces 过滤器导航之后未调用

JSF/PrimeFaces ajax 更新破坏了 jQuery 事件侦听器函数绑定

通过 Ajax 更新 JSF 组件后,JavaScript/jQuery 事件侦听器不起作用

如何在 Ajax 请求期间在 JSF2 中动态添加组件