2011-06-23 27 views
19

Tengo un componente compuesto JSF 2 que emplea algún comportamiento Ajax. Deseo agregar un método listener a la etiqueta <f:ajax> dentro de mi componente compuesto, pero el método listener se debe proporcionar como <composite:attribute> en el <composite:interface>.JSF 2 - ¿Cómo puedo agregar un método de escucha Ajax a la interfaz compuesta de componentes?

La etiqueta <f:ajax> dentro de mi componente compuesto está actualmente programado en forma fija a un oyente como esto:

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

El método detector en el frijol tiene esta firma:

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

Quiero que el compuesto El componente es algo así para que la página pueda proporcionar su propio método de evento, pero no puedo encontrar la sintaxis correcta para la interfaz.

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

¿Cómo puedo hacer esto?

actualiza con SOLUCIÓN:

Tomé el enfoque sugerido por BalusC y funciona muy bien. Los fragmentos relevantes son:

La declaración de interfaz en el componente compuesto

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

La etiqueta Ajax utilizada en mi pieza compuesta

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

El lugar en mi página en la que utilizo el componente compuesto

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

Y el método en mi respaldo Bean

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

gracias por la solución, realmente me ayuda. –

+0

¿Es posible hacer eso pasando un método con un parámetro? Es decir, en lugar de pasar # {myBackingBean.updatedEventListenerXYZ} como en el ejemplo, pase algo como # {myBackingBean.myMethod (cc.attrs.myparam)} – dcalap

Respuesta

21

Si puede deshacerse del argumento AjaxBehaviorEvent,

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

continuación, puede utilizar

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

Si el argumento es obligatorio (para el registro?), Entonces usted necesita para volver a especificar el atributo de la siguiente manera

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

Sin embargo, esto aquí no funciona como se esperaba con

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

en 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 

No estoy seguro de si esto es un error o no. Para eso, necesitaría invertir más tiempo para resolverlo. Sin embargo, fue muy útil al crear un componente de respaldo que obtiene el MethodExpression de los atributos e invoca con el número correcto de argumentos. He aquí un ejemplo patada de salida completa:

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

con

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

} 

En cualquier caso, creo que el componente de respaldo pone puertas abiertas para las nuevas formas de alcanzar el requisito funcional que ha tenido en cuenta todos modos;)

+1

Gracias por esto. Encontré muchos ejemplos de cómo declarar un método de acción en una interfaz CC, pero ninguno para declarar un método de escucha. ¿Es la parte 'listener()' de 'method-signature' un valor especial que se entiende por JSF? –

+4

El nombre del método no es relevante. Incluso puedes ponerle el nombre 'xyz()'. Fue solo un ejemplo de autodescripción. Lo más importante es el nombre de clase calificado completo del retorno del método y los tipos de parámetros. – BalusC

+0

Exactamente lo que estaba buscando;) – boblemar

1

Aquí hay un ejemplo de cómo usar la interfaz para establecer un atributo y referenciarlo dentro de la implementación. Debe definir la firma de método del método que será llamado. Esto informa al controlador de componente compuesto que existe un valor de método, en lugar de una expresión de valor, que figura en la expresión # {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> 
+0

¿En qué entorno lo probó/usó? Esto falla para mí en GF 3.1 y Tomcat 7 con Mojarra 2.1.1. – BalusC

Cuestiones relacionadas