Ajax视图更新在带有专用Java类的自定义组件中无法正常工作 [英] Ajax view updates doesn't work properly in custom components with dedicated java class

查看:46
本文介绍了Ajax视图更新在带有专用Java类的自定义组件中无法正常工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要解决的问题比这里要描述的要大,但是由于难以解释和调试我的代码,因此我创建了一个较小的问题(愚蠢的问题)以重现相同的错误.

I have a bigger problem than the one I'll describe here, but since it would be difficult to explain and debug my code, I created a smaller (and dumb) problem to reproduce the same error.

在我看来,我想打印一个字符串列表,该列表是基于偏移量计算的.在我的Java类中,我有一个巨大的静态字符串数组.为了计算我的字符串列表(将在页面中显示),我将静态数组中的值从"offset"的值复制到列表中.

In my view I want to print a list of strings, this list is calculated based in an offset. In my java class I have a huge static array of Strings. To calculate my list of strings (that will be printed in the page) I copy the values in my static array to my list, starting from the value of "offset".

在我的页面中,我还有一个递增偏移量"按钮,该按钮通过ajax请求递增偏移量"的值并更新结果(该列表将根据新的偏移量重新计算).

In my page I also have a button "Increment offset" that increments the value of "offset" via an ajax request and update the results (the list is recalculated based in the new offset).

我的问题是:当我单击按钮增量偏移量"时,服务器端代码被执行,偏移量增加了,但是更新时发生了问题:显示当前偏移量的文本被更新为的新值. 偏移",但字符串列表不会发生相同的情况,它会使用旧值进行更新,并且即使我执行第二个请求,也会考虑新值,而第二个请求中计算出的值仅会在第三次更新,使视图中的字符串列表始终延迟一个请求.

My problem is: when I click the button "Increment offset", the server-side code is executed, the offset is incremented but a problem happens on the update: the text showing the current offset gets updated with the new value of "offset", but the same doesn't happen to the list of strings, it is updated with the old values, and the new values are considered just if I do a second request, the values calculated in the second request are considered just in a 3rd update, making the list of strings in the view always one request late.

查看此图像(在新选项卡中将其打开以使其更大):

See this image (open it in a new tab to so it larger):

让我们看看一些代码...

Let's see some code...

按钮:

<h:form>
  <h:commandButton value="Increment offsets">
    <f:ajax listener="#{test.incrementOffset()}" render=":stringList" />
  </h:commandButton>
</h:form>

字符串列表:

<h:panelGroup id="stringList">
  <h1>Offsets increment = #{test.offset}</h1>
  <comp:stringPrinter offset="#{test.offset}" />
</h:panelGroup>

这是我在标题中讨论的组件.字符串列表将不被页面计算或打印.它将被计算并打印在复合组件中.

There it is the component I talked about in the title. The list of strings will not be calculated nor printed by the page. It will be calculated and printed in a composite component.

在转到复合组件的代码之前,这是在视图中使用的托管bean"Test":

Before going to the code of the composite component, here's the managed bean "Test", used in the view:

@ManagedBean(name="test")
@ViewScoped
public class Test implements Serializable {
  private int offset;

  public int getOffset() {
    return offset;
  }

  public void incrementOffset(){
     offset++;
  }
}

复合组件:正如我所说的那样简单,它仅接收偏移"作为参数并打印字符串列表.请参见下面的查看代码:

The composite component: simple as I said, it just receives "offset" as parameter and print a list of strings. See the view code below:

<h:body>
  <composite:interface componentType="stringPrinter">
    <composite:attribute name="offset" type="java.lang.Integer" required="true" />
  </composite:interface>
  <composite:implementation>
    <ul>
      <ui:repeat value="#{cc.list}" var="string">
        <li>#{string}</li>
      </ui:repeat>
    </ul>
  </composite:implementation>
</h:body>

这是字符串列表的计算方式:

And this is how the list of strings is calculated:

@FacesComponent(value="stringPrinter")
public class StringPrinter extends UINamingContainer implements Serializable {
  private ArrayList<String> list;

  private static String[] LIPSUM = "...".split(" "); // "..." is not the actual string, the string is irrelevant (it has more than 400 words separated by spaces).
  private static int ARRAY_SIZE = 12;

  private void generateList(){
    int offset = (Integer) getAttributes().get("offset");
    int position = offset;
    list = new ArrayList<String>();
    for (int i = 0; i < ARRAY_SIZE; i++){
      list.add(LIPSUM[position++ % LIPSUM.length]);
    }
  }

  public ArrayList<String> getList() {
    if (null == list) generateList();
      return list;
    }
  }
}

如果我不使用组件,则不会发生此问题.当在父视图中计算列表并且组件没有附加Java类时,不会发生此问题.但是,在我真正的问题中,我不需要打印字符串,我确实需要通过复合组件来处理某些事情,因此有必要为其提供专用的类.

The problem doesn't happen if I don't use a component. The problem doesn't happen when the list is calculated in the parent view and the component has no java class attached. But in my real problem I don't need to print Strings, I really need things to get processed by a composite-component, making it necessary to have a dedicated class for it.

一些调试:

ui:repeat标记可能是问题所在.如果不是打印列表(需要ui:repeat),而是打印列表中的第一个String,则所有内容都将在正确的时间正确更新.在不使用ui:repeats的情况下,查看复合组件的代码:

The ui:repeat tag could be the problem. If instead of printing a list (which would require an ui:repeat) I needed to print the first String in the list, everything would get correctly updated, in the correct time. See the code of the composite component without the use of ui:repeats:

<h:body>
  <composite:interface componentType="stringPrinter">
    <composite:attribute name="offset" type="java.lang.Integer" required="true" />
  </composite:interface>
  <composite:implementation>
    <h:outputText value="#{cc.list.get(0)}" />
  </composite:implementation>
</h:body>

上面的代码可以工作,但这不是我需要做的,我确实需要使用ui:repeat.我也尝试过c:forEach,结果相同.

The code above works, but it's not what I need to do, I really need to use ui:repeat. I also tried c:forEach, same result.

为了进行进一步的调试,我向自己提出了一个问题:组件中的方法getList()在执行页面中的方法offsetOffset()之后会被调用吗?".这是我得到的日志:

For further debugging I made myself the question: "does the method getList(), in the component, get called after the method incrementOffset(), in the page, is executed?". This is the log I got:

*** GET LIST CALLED!
*** GET LIST CALLED!
*** OFFSET INCREMENTED!
*** GET LIST CALLED!
*** GET LIST CALLED!

我使用System.out.flush()来防止消息被缓冲.似乎在方法执行后调用了getList(),我看不出有任何理由用旧值更新视图.无论如何,实际上在执行crementOffset()之前两次调用它,而在执行两次之后又调用它,这有点奇怪.

I used System.out.flush() to prevent messages from being buffered. It seems getList() is called after the method is executed, I don't see any reason for the view to be updated with old values. Anyway, the fact it is called two times just before incrementOffset() is executed and two times after is a bit strange.

此问题影响Mojarra 2.1.7、2.1.22、2.1.23和2.2.0.我没有测试其他版本.

This problem affects Mojarra 2.1.7, 2.1.22, 2.1.23 and 2.2.0. I did not test the other versions.

我将通过进一步的调试来更新此帖子.

I'm gonna update this post with further debug.

要下载Maven项目并自己进行测试,请执行以下操作: zip文件

To download the Maven project and test it for yourself: zip file

预先感谢您的回答.

推荐答案

我发现了问题所在.我认为当组件被更新时,它会被完全重新处理,但是在某些情况下不会发生.出于某种原因,似乎在使用ui:repeat时,JSF尝试延迟组件重置.

I discovered where the problem is. I thought that when a component gets updated it was entirely reprocessed, but it doesn't happen in some occasions. For some reason, it seems that when ui:repeat is used, JSF tries to retard the component reset.

一种解决方案是在调用getList()时显式重新计算列表:

A solution is to explicitly recalculate the list when getList() is called:

以前是这样的:

  public ArrayList<String> getList() {
    if (null == list) generateList();
      return list;
    }
  }

如果我删除了null验证,那么它将起作用.

If I remove the verification for null it works.

在我看来,这就是发生的事情:更新组件时,列表仍然具有之前计算出的值,它不为null,并且不进行任何重新计算.在一个请求与另一个请求之间的时间内,该组件正在被重建.重置组件后,list等于null,并且对getList()进行了重新计算,从而使视图始终延迟一个请求.

In my opinion that's what was happening: when the component was updated the list still had the values calculated before, it was not null and no recalculation was done. In the time between one request and another, the component was getting reconstructed. With the component reseted, list was equal to null and the recalculation was done on getList(), making the view always one request late.

更好的解决方案是在组件的视图中使用f:event而不是在每次获取时重新计算列表.

A much better solution is to use f:event in the component's view instead of recalculating the list on every get.

<f:metadata>
  <f:event type="preRenderComponent" listener="#{cc.generateList}" />
</f:metadata>

这篇关于Ajax视图更新在带有专用Java类的自定义组件中无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆