首页 文章

包含带ajax的inputText的复合组件

提问于
浏览
1

The way it works

为了找到解决方案,我发现我忘记了使用组件中的主要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>
  • beans
@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>

最后,感谢所有给我一些提示并帮助我找到错误 .

Improvements step 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>
  • 托管bean:
@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响应(在日志窗口中没有结果) . 这让我感到困惑,我怎么能让它运作起来?

Improvements step 1 (old)

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

<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}" />

结果确实没有任何反应 . 我该怎么做才能做到这一点?

The original post*

我想让我的复合组件与AJAX一起工作 . 我google了很多,甚至在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}" />

我的 beans 子:

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

BTW . 通过复合组件要复杂得多,我将这个例子简化为相关部分 .

2 回答

  • 3

    <cc:clientBehavior> 应该有效 . 你的 targets 只是错的 .

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

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

    因此,如此

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

    如果我们查看您的复合组件定义(特别是在 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" />
    

相关问题