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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6453842/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 06:05:12  来源:igfitidea点击:

JSF 2 - How can I add an Ajax listener method to composite component interface?

javaajaxjsf-2listenercomposite-component

提问by Jim Tough

I have a JSF 2 composite component that employs some Ajax behavior. I want to add a listenermethod to the <f:ajax>tag inside my composite component, but the listenermethod should be provided as a <composite:attribute>in the <composite:interface>.

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

The <f:ajax>tag inside my composite component is currently hard-coded to a listener like this:

<f:ajax>我的复合组件中的标签目前硬编码到一个监听器,如下所示:

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

The listener method on the bean has this signature:

bean 上的侦听器方法具有以下签名:

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

I want the composite component to be something like this so the page can supply its own event method, but I can't figure out the correct syntax for the interface.

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

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

How can I do this?

我怎样才能做到这一点?

UPDATED WITH SOLUTION:

更新解决方案:

I took the approach suggested by BalusC and it works great. The relevant snippets are:

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

The interface declaration in the composite component

复合组件中的接口声明

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

The Ajax tag used in my composite component

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

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

The place in my page where I use the composite component

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

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

And the method on my backing bean

以及我的支持 bean 上的方法

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

采纳答案by BalusC

If you canget rid of the AjaxBehaviorEventargument,

如果你摆脱AjaxBehaviorEvent争论,

public void genericAjaxEventLogger() {
    // ...
}

then you can use

然后你可以使用

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

If the argument is mandatory (for logging?), then you need to re-specify the attribute as follows

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

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

However, this does here not work as expected with

但是,这在这里不能按预期工作

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

on GF 3.1 + Mojarra 2.1.1:

在 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

I'm not sure if this is a bug or not. For that I'd need to invest some more time to naildown it. However, it was workaroundable by creating a backing component which gets the MethodExpressionfrom the attributes and invokes with the right number of arguments. Here's a complete kickoff example:

我不确定这是否是一个错误。为此,我需要投入更多时间来确定它。但是,它可以通过创建一个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>

with

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 });
    }

}

Regardless, I believe that the backing component puts doors open for new ways to achieve the functional requirement you have had in mind anyway ;)

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

回答by Jason

Here is an example on how to use the interface to set an attribute and reference it within the implementation. You must define the method-signature of the method that will be called. This informs the composite component handler that there is a method value, rather than a value expression, contained in the #{cc.attrs.ajaxEventListener} expression.

下面是一个关于如何使用接口设置属性并在实现中引用它的示例。您必须定义将被调用的方法的方法签名。这会通知复合组件处理程序,#{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>