3

原始问题如下,但我想出了一个更简单的例子来演示这个问题,并认为它应该放在顶部。

无论如何,似乎ui:repeat在检查父元素是否实际呈现之前处理了标签。要重新创建它,这里是 facelet (minimalTest.xhtml):

<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">
<h:head>
  <title>Test JSF &lt;ui:repeat&gt; inside &lt;h:panelGroup rendered=&quot;false&quot;&gt;</title>
</h:head>
<h:body>
  <h:form>
    <h1>Testing</h1>
    <h:panelGroup rendered="false">
      <span>#{minimalTestBean.alsoThrowsException}</span>
      <ul>
        <ui:repeat value="#{minimalTestBean.throwsException}" var="item">
          <li>#{item}</li>
        </ui:repeat>
      </ul>
    </h:panelGroup>
  </h:form>
</h:body>
</html>

使用这个bean(MinimalTestBean.java):

package com.lucastheisen.beans;


import java.io.Serializable;
import java.util.List;


import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;


@ManagedBean
@ViewScoped
public class MinimalTestBean implements Serializable {
    private static final long serialVersionUID = 9045030165653014015L;

    public String getAlsoThrowsException() {
        throw new RuntimeException( "rendered is false so this shouldnt get called either" );
    }

    public List<String> getThrowsException() {
        throw new RuntimeException( "rendered is false so this shouldnt get called" );
    }
}

从这个例子中你可以看到h:panelGroup包含 的ui:repeat是静态设置的rendered=false,我假设这意味着其中的任何 EL 表达式都h:panelGroup不会被执行。EL 表达式只是调用引发 RuntimeException 的 getter。但是,ui:repeat它实际上是为其列表调用 getter,从而导致异常,即使它不应该首先呈现。如果您注释掉该ui:repeat元素,则不会引发异常(即使其他 EL 表达式仍保留在 中h:panelGroup),正如我所期望的那样。

在 stackoverflow 上阅读其他问题让我相信这可能与经常提到的鸡/蛋问题有关,但我不确定具体原因,也不知道该怎么做。我想将 PARTIAL_STATE_SAVING 设置为 false 可能会有所帮助,但希望避免内存影响。

----原始问题----

<h:panelGroup rendered="#{modeXXX}">基本上,我有一个页面,它使用环绕<ui:include src="pageXXX.xhtml" />(根据这个答案)有条件地呈现部分。问题是,如果其中一个 pageXXX.xhtml 有<ui:repeat>它的内部,即使包含 .xhtml 似乎也会被<h:panelGroup>处理rendered=false。这是一个问题,因为我的某些部分依赖于已被其他部分初始化,而这些部分应该在它们之前访问。为什么包含的 pageXXX.xhtml 得到处理?

这是一个令人痛苦的错误,很难归结为一个小例子,但这是我能构建的最简单的案例来证明这个问题。首先是一个基本页面:

<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">
<h:head>
  <title>Test JSF &lt;ui:include&gt;</title>
</h:head>
<h:body>
  <h:form>
    <h1>#{testBean.title}</h1>
    <h:panelGroup rendered="#{testBean.modeOne}">
      <ui:include src="modeOne.xhtml" />
    </h:panelGroup>
    <h:panelGroup rendered="#{testBean.modeTwo}">
      <ui:include src="modeTwo.xhtml" />
    </h:panelGroup>
  </h:form>
</h:body>
</html>

如您所见,此页面将根据 testBean bean 中的值有条件地包含 modeOne 页面或 modeTwo 页面。然后你有 modeOne (默认):

<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">
<ui:composition>
  <span>Okay, I&apos;m ready.  Take me to </span>
  <h:commandLink action="#{testBean.setModeTwo}">mode two.</h:commandLink>
</ui:composition>
</html>

在我的现实世界应用程序中,这将是一个设置 modeTwo 所需内容的页面。设置完成后,此页面上的操作将引导您进入 modeTwo:

<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">
  <ui:composition>
    <div>Here is your list:</div>
    <ui:repeat value="#{testBeanToo.list}" var="item">
      <div>#{item}</div>
    </ui:repeat>
  </ui:composition>
</html>

modeTwo 页面基本上在 ui:repeat 中显示 modeOne 页面的详细信息,因为实际信息位于集合中。主要托管bean(TestBean):

package test.lucastheisen.beans;


import java.io.Serializable;


import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;


@ManagedBean
@ViewScoped
public class TestBean implements Serializable {
    private static final long serialVersionUID = 6542086191355916513L;
    private Mode mode;
    @ManagedProperty( value="#{testBeanToo}" )
    private TestBeanToo testBeanToo;

    public TestBean() {
        System.out.println( "constructing TestBean" );
        setModeOne();
    }

    public String getTitle() {
        System.out.println( "\ttb.getTitle()" );
        return mode.getTitle();
    }

    public boolean isModeOne() {
        return mode == Mode.One;
    }

    public boolean isModeTwo() {
        return mode == Mode.Two;
    }

    public void setModeOne() {
        this.mode = Mode.One;
    }

    public void setModeTwo() {
        testBeanToo.getReadyCauseHereICome();
        this.mode = Mode.Two;
    }

    public void setTestBeanToo( TestBeanToo testBeanToo ) {
        this.testBeanToo = testBeanToo;
    }

    private enum Mode {
        One("Mode One"),
        Two("Mode Two");

        private String title;

        private Mode( String title ) {
            this.title = title;
        }

        public String getTitle() {
            return title;
        }
    }
}

是所有主要数据的 bean,而 TestBeanToo bean 将用于详细信息:

package test.lucastheisen.beans;


import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;


import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;


@ManagedBean
@ViewScoped
public class TestBeanToo implements Serializable {
    private static final long serialVersionUID = 6542086191355916513L;
    private ObjectWithList objectWithList = null;

    public TestBeanToo() {
        System.out.println( "constructing TestBeanToo" );
    }

    public String getTitle() {
        System.out.println( "\ttb2.getTitle()" );
        return "Test Too";
    }

    public List<String> getList() {
        System.out.println( "\ttb2.getList()" );
        return objectWithList.getList();
    }

    public void getReadyCauseHereICome() {
        System.out.println( "\ttb2.getList()" );
        objectWithList = new ObjectWithList();
    }

    public class ObjectWithList {
        private List<String> list;

        public ObjectWithList() {
            list = new ArrayList<String>();
            list.add( "List item 1" );
            list.add( "List item 2" );
        }

        public List<String> getList() {
            return list;
        }
    }
}
4

1 回答 1

2

<ui:repeat>在渲染视图时不检查rendered自身的属性(实际上没有)及其父级。考虑改用Tomahawk's <t:dataList>

于 2011-09-25T16:51:22.183 回答