简体   繁体   English

包含带ajax的inputText的复合组件

[英]Composite Component containing inputText with ajax

The way it works 它的工作方式

Seeking hard for the solution, I found that I just forgot the leading h:head tags in my usage component. 为了找到解决方案,我发现我忘记了使用组件中的主要h:head标签。 Adding them made all the errors disappear. 添加它们会使所有错误消失。 So for a complete solution this here is my last code: 所以对于一个完整的解决方案,这是我最后的代码:

  • the composite component 复合组件
<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>

  • the bean 豆子
@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;
}
}
  • the calling xhtml file 调用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>

Finally, thanks to all that gave me some hints and helped me finding out about the bugs. 最后,感谢所有给我一些提示并帮助我找到错误。

Improvements step 2 改进步骤2

Redesigning the component now I have this: 现在重新设计组件我有这个:

  • composite component: 复合组件:
<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>

  • managed bean: 托管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;
}

} }

  • usage: 用法:
<!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>

Nevertheless no ajax response is done to my component outside of the form (no results in the log window). 然而,在表单之外没有对我的组件进行ajax响应(在日志窗口中没有结果)。 This is confusing me, how can I make it work? 这让我感到困惑,我怎么能让它运作起来?

Improvements step 1 (old) 改进步骤1(旧)

So I tried to improve my code, changing the composite component to 所以我尝试改进我的代码,将复合组件更改为

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

and my call would be 我的电话会是

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

with the result that exactly nothing happens. 结果确实没有任何反应。 What may I do to make this work? 我该怎么做才能做到这一点?

The original post* 原帖*

I'd like to make my composite component working with AJAX. 我想让我的复合组件与AJAX一起工作。 I googled a lot, found some solutions even here on stackoverflow, but they all seemed to work with buttons only. 我google了很多,甚至在stackoverflow上找到了一些解决方案,但它们似乎只能用于按钮。 Here I have an inputText component, how can I give my component an AJAX event listener? 这里我有一个inputText组件,如何为组件提供一个AJAX事件监听器? Executing my example (see below) gives this error: 执行我的示例(见下文)会出现此错误:

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)

My composite component: 我的复合组件:

<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 call: 我的电话:

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

My Bean: 我的豆子:

@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. BTW。 by composite component is much more complex, I reduced this example to the relevant parts. 通过复合组件要复杂得多,我将这个例子简化为相关部分。

The <cc:clientBehavior> should work. <cc:clientBehavior>应该可以工作。 Your targets is only wrong. 你的targets只是错误的。

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

It must be relative to the composite itself, not to the parent/viewroot or whatever (as that would theoretically require editing the composite component everytime when you put it in a different naming container parent!). 它必须是相对于复合本身,而不是父/ viewroot或其他任何东西(因为理论上每次将它放在不同的命名容器父级中时都需要编辑复合组件!)。

Thus, so 因此,如此

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

If we look at your composite component definition (especially at ajaxListener attribute) you defined a method with AjaxBehaviorEvent (which is correct signature for this kind of event listener) but in use of your component you defined an attribute as: 如果我们查看您的复合组件定义(特别是在ajaxListener属性中),您使用AjaxBehaviorEvent (这种事件侦听器的正确签名)定义了一个方法,但在使用您的组件时,您将属性定义为:

ajaxListener="#{test.listen}"

So, no arguments - and that is place where this exception is thrown (in trying to find arguments of method). 所以,没有参数 - 这就是抛出此异常的地方(在尝试查找方法的参数时)。

With this your use case, you should change your component definition little bit. 有了这个用例,您应该稍微更改组件定义。 So change: 所以改变:

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

to something like this: 这样的事情:

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

Also change your listener attribute in f:ajax tag to: 还要将f:ajax标记中的listener属性更改为:

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

Finally change the way how your composite component is used: 最后更改复合组件的使用方式:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM