Jsf Ajax视图更新不';t使用专用java类在自定义组件中正常工作

Jsf Ajax视图更新不';t使用专用java类在自定义组件中正常工作,jsf,mojarra,Jsf,Mojarra,我有一个比我将在这里描述的问题更大的问题,但是由于很难解释和调试我的代码,所以我创建了一个更小(而且愚蠢)的问题来重现相同的错误 在我看来,我想打印一个字符串列表,这个列表是基于偏移量计算的。在我的java类中,我有一个巨大的静态字符串数组。为了计算字符串列表(将打印在页面中),我将静态数组中的值复制到列表中,从“offset”的值开始 在我的页面中,我还有一个按钮“incrementoffset”,它通过ajax请求增加“offset”的值,并更新结果(根据新的偏移量重新计算列表) 我的问题是

我有一个比我将在这里描述的问题更大的问题,但是由于很难解释和调试我的代码,所以我创建了一个更小(而且愚蠢)的问题来重现相同的错误

在我看来,我想打印一个字符串列表,这个列表是基于偏移量计算的。在我的java类中,我有一个巨大的静态字符串数组。为了计算字符串列表(将打印在页面中),我将静态数组中的值复制到列表中,从“offset”的值开始

在我的页面中,我还有一个按钮“incrementoffset”,它通过ajax请求增加“offset”的值,并更新结果(根据新的偏移量重新计算列表)

我的问题是:当我点击“增量偏移”按钮时,服务器端代码被执行,偏移量被增加,但在更新时出现问题:显示当前偏移量的文本被更新为新值“偏移量”,但字符串列表没有出现同样的情况,它被更新为旧值,如果我执行第二个请求,就会考虑新值,第二个请求中计算的值会在第三次更新中考虑,这使得视图中的字符串列表总是延迟一个请求

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

让我们看看一些代码

按钮:

<h:form>
  <h:commandButton value="Increment offsets">
    <f:ajax listener="#{test.incrementOffset()}" render=":stringList" />
  </h:commandButton>
</h:form>
复合组件:正如我所说的简单,它只接收“offset”作为参数并打印字符串列表。请参见下面的视图代码:

<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>
我使用System.out.flush()防止消息被缓冲。似乎getList()是在方法执行后调用的,我看不出有任何理由用旧值更新视图。无论如何,它在incrementOffset()执行之前被调用了两次,之后又被调用了两次,这有点奇怪

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

我将用进一步的调试更新这篇文章

要下载Maven项目并自己测试它,请执行以下操作:


提前感谢您的回答。

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

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

以前是这样的,

  public ArrayList<String> getList() {
    if (null == list) generateList();
      return list;
    }
  }
public ArrayList getList(){
if(null==list)generateList();
退货清单;
}
}
如果我删除了null的验证,它就会工作

在我看来,情况就是这样:当组件更新时,列表中仍然有以前计算过的值,它不是空的,并且没有进行重新计算。在一个请求和另一个请求之间,组件被重建。重置组件后,list等于null,并在getList()上重新计算,使视图始终延迟一个请求


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

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

不需要在
内。
@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;
    }
  }
}
<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>
*** GET LIST CALLED!
*** GET LIST CALLED!
*** OFFSET INCREMENTED!
*** GET LIST CALLED!
*** GET LIST CALLED!
  public ArrayList<String> getList() {
    if (null == list) generateList();
      return list;
    }
  }
<f:metadata>
  <f:event type="preRenderComponent" listener="#{cc.generateList}" />
</f:metadata>