jsf 2 - 包含 ajax 的输入文本的复合组件



它的工作方式

努力寻找解决方案,我发现我只是忘记了使用组件中的前导 h:head 标签。添加它们使所有错误消失。因此,对于完整的解决方案,这是我的最后一段代码:

  • 复合组件
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface>
    <composite:attribute name="value" required="true"  />
    <composite:attribute name="size" required="false" default="20" />
    <composite:clientBehavior name="change" event="change" targets="input" />
</composite:interface>
<composite:implementation>
    <h:inputText id="input" value="#{cc.attrs.value}" size="#{cc.attrs.size}" />
</composite:implementation>

  • 豆子
@ManagedBean
@SessionScoped
public class MyBean
{
private String value1;
private String value2;
private String value3;
public String exec()
{
    this.value2 = value3;
    return "";
}
public void listenAjax(AjaxBehaviorEvent e)
{
    UIInput i = (UIInput) e.getComponent();     
    value2 = (String) i.getValue();
    System.out.println("ajax value = " + i.getValue());
    System.out.println("value1 = " + value1);
    System.out.println("value2 = " + value2);
    System.out.println("value3 = " + value3);
}
public String getValue3()
{
    return value3;
}
public String getValue2()
{
    return value2;
}
public String getValue1()
{
    return value1;
}
public void setValue1(String value1)
{
    this.value1 = value1;
}
public void setValue3(String value3)
{
    this.value3 = value3;
}
}
  • 调用 XHTML 文件
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:test="http://java.sun.com/jsf/composite/test">
<h:head>
</h:head>
<h:body>
<h:form id="form">
    <h:panelGrid columns="2">
        <h:outputText value="value3" />
        <h:inputText value="#{myBean.value3}">
            <f:ajax event="change" render="see" listener="#{myBean.listenAjax}" />
        </h:inputText>
        <h:outputText value="value1" />
        <test:test value="#{myBean.value1}">
            <f:ajax event="change" render=":form:see" listener="#{myBean.listenAjax}" />
        </test:test>
        <h:outputText value="value2" />
        <h:outputText id="see" value="#{myBean.value2}" />
        <h:outputText value="" />
        <h:commandButton action="#{myBean.exec}" value="set" />
    </h:panelGrid>
</h:form>
</h:body>
</html>

最后,感谢所有给我一些提示并帮助我发现错误的人。

改进步骤 2

重新设计组件,现在我有这个:

  • 复合组件:
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface>
    <composite:attribute name="value" required="true"  />
    <composite:attribute name="size" required="false" default="20" />
    <composite:clientBehavior name="change" event="change" targets="input" />
</composite:interface>
<composite:implementation>
    <h:inputText id="input" value="#{cc.attrs.value}" size="#{cc.attrs.size}" />
</composite:implementation>

  • 托管豆:
@ManagedBean
@SessionScoped
public class MyBean {
private String value1;
private String value2;
private String value3;
public String exec()
{
    this.value2 = value3;
    return "";
}
public void listen(AjaxBehaviorEvent e)
{
    UIInput i = (UIInput) e.getComponent();     
    value2 = (String) i.getValue();
    System.out.println("ajax value = " + i.getValue());
    System.out.println("value1 = " + value1);
    System.out.println("value2 = " + value2);
    System.out.println("value3 = " + value3);
}
public String getValue3()
{
    return value3;
}
public String getValue2()
{
    return value2;
}
public String getValue1()
{
    return value1;
}
public void setValue1(String value1)
{
    this.value1 = value1;
}
public void setValue3(String value3)
{
    this.value3 = value3;
}

}

  • 用法:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:test="http://java.sun.com/jsf/composite/test">
<h:body>
<h:form>
    <test:test value="#{myBean.value1}">
        <f:ajax event="change" render=":out" listener="#{myBean.listen}" />
    </test:test>
    <h:inputText value="#{myBean.value3}">
        <f:ajax event="change" render=":out" listener="#{myBean.listen}" />
    </h:inputText>
    <h:commandButton action="#{myBean.exec}" value="set" />
</h:form>
<h:outputText id="out" value="#{myBean.value2}" />
</h:body>
</html>

尽管如此,在表单之外没有对我的组件进行 ajax 响应(日志窗口中没有结果(。这让我感到困惑,我怎样才能让它工作?

改进步骤 1(旧(

所以我试图改进我的代码,将复合组件更改为

<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface>
    <composite:attribute name="value" required="true" />
    <composite:attribute name="size" required="false" default="20" />
    <composite:clientBehavior name="change" event="change" targets="#{cc.clientId}:input" />
</composite:interface>
<composite:implementation>
    <h:inputText id="input" value="#{cc.attrs.value}" size="#{cc.attrs.size}" />
</composite:implementation>

我的电话将是

<my:test value="#{test.value1}">
  <f:ajax event="change" render=":out" listener="#{test.listen}" />
</my:test>
<h:outputText id="out" value="#{test.value2}" />

结果什么也没发生。我该怎么做才能完成这项工作?

原始帖子*

我想让我的复合组件与 AJAX 一起工作。我用谷歌搜索了很多,甚至在stackoverflow上找到了一些解决方案,但它们似乎都仅适用于按钮。这里我有一个inputText组件,如何为我的组件提供 AJAX 事件侦听器?执行我的示例(见下文(会出现此错误:

com.sun.faces.lifecycle.InvokeApplicationPhase execute
WARNING: 0
java.lang.ArrayIndexOutOfBoundsException: 0
at org.apache.el.parser.AstValue.convertArgs(AstValue.java:320)
at org.apache.el.parser.AstValue.invoke(AstValue.java:274)
at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:274)
at com.sun.faces.facelets.el.ContextualCompositeMethodExpression.invoke(ContextualCompositeMethodExpression.java:187)
at com.sun.faces.facelets.tag.TagAttributeImpl$AttributeLookupMethodExpression.invoke(TagAttributeImpl.java:473)
at com.sun.faces.facelets.tag.jsf.core.AjaxBehaviorListenerImpl.processAjaxBehavior(AjaxHandler.java:459)
at javax.faces.event.AjaxBehaviorEvent.processListener(AjaxBehaviorEvent.java:113)
at javax.faces.component.behavior.BehaviorBase.broadcast(BehaviorBase.java:106)
at javax.faces.component.UIComponentBase.broadcast(UIComponentBase.java:809)
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:800)
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1292)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:181)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:645)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)

我的复合组件:

<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface>
    <composite:attribute name="value" required="true" />
    <composite:attribute name="size" required="false" default="20" />
    <composite:attribute name="enableAjax" required="false" default="false" />
    <composite:attribute name="ajaxRender" required="false" />
    <composite:attribute name="ajaxListener" required="false" method-signature="void listen(javax.faces.event.AjaxBehaviorEvent)" />
</composite:interface>
<composite:implementation>
    <h:inputText id="input" value="#{cc.attrs.value}" size="#{cc.attrs.size}">
        <f:ajax event="change" render="#{cc.attrs.ajaxRender}" listener="#{cc.attrs.ajaxListener}" disabled="#{!cc.attrs.enableAjax}" />
    </h:inputText>
</composite:implementation>

我的电话:

<my:test value="#{test.value1}" ajaxRender=":out" ajaxListener="#{test.listen}" enableAjax="true" />
<h:outputText id="out" value="#{test.value2}" />

我的豆子:

@ManagedBean
@SessionScoped
public class Test {
  private String value1;
  private String value2;
  ...
  public void listen(AjaxBehaviorEvent e)
  {
    value2 = (String) ((UIInput) e.getComponent()).getValue();
  }
  ... (getter & setter)
}

顺便说一下,通过复合组件要复杂得多,我将此示例简化为相关部分。

<cc:clientBehavior>应该可以工作。你的targets只是错了。

<composite:clientBehavior name="change" event="change" targets="#{cc.clientId}:input" />

它必须相对于复合本身,而不是父/视根或其他任何东西(因为理论上,当您将复合组件放入不同的命名容器父级时,每次都需要编辑复合组件!

因此,所以

<composite:clientBehavior name="change" event="change" targets="input" />

如果我们查看您的复合组件定义(尤其是在 ajaxListener 属性中(,您使用 AjaxBehaviorEvent 定义了一个方法(这是此类事件侦听器的正确签名(,但在使用您的组件时,您将一个属性定义为:

ajaxListener="#{test.listen}"

所以,没有参数 - 这就是抛出此异常的地方(在尝试查找方法的参数时(。

有了这个用例,你应该稍微改变一下你的组件定义。所以改变:

<composite:attribute name="ajaxListener" required="false" method-signature="void listen(javax.faces.event.AjaxBehaviorEvent)" />

像这样:

<composite:attribute name="bean" required="false" type="java.lang.Object" />
<composite:attribute name="ajaxListener" required="false" type="java.lang.String" />

此外,将 f:ajax 标记中的listener属性更改为:

listener="#{cc.attrs.bean[cc.attrs.ajaxListener]}"

最后,更改复合组件的使用方式:

<my:test value="#{test.value1}" ajaxRender=":out" ajaxListener="listen" bean="#{test}" enableAjax="true" />

最新更新