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

前端 未结 2 990
借酒劲吻你
借酒劲吻你 2020-12-08 08:20

I have a JSF 2 composite component that employs some Ajax behavior. I want to add a listener method to the tag inside my composite c

相关标签:
2条回答
  • 2020-12-08 08:30

    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: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>
    
    0 讨论(0)
  • 2020-12-08 08:47

    If you can get rid of the AjaxBehaviorEvent argument,

    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:

    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 MethodExpression from the attributes and invokes with the right number of arguments. Here's a complete kickoff example:

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

    0 讨论(0)
提交回复
热议问题