Ответ 1
<ui:repeat>
не проверяет атрибут rendered
(он фактически отсутствует) и его родители, когда представление должно быть отображено. Вместо этого используйте Tomahawk <t:dataList>
.
Оригинальный вопрос ниже, но, поскольку я придумал более минимальный пример, чтобы продемонстрировать эту проблему, и подумал, что он должен идти вверху.
В любом случае, кажется, что теги ui:repeat
обрабатываются перед проверкой, чтобы убедиться, что на самом деле отображаются исходные элементы. Чтобы воссоздать это, вот макет (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 <ui:repeat> inside <h:panelGroup rendered="false"></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. Тем не менее, ui:repeat
на самом деле вызывает геттер для своего списка, тем самым вызывая исключение, даже если он не должен получать визуализацию в первую очередь. Если вы прокомментируете элемент ui:repeat
, никакие исключения не будут выбрасываться (хотя другое выражение EL остается в h:panelGroup
), как и следовало ожидать.
Чтение других вопросов здесь в stackoverflow приводит к тому, что я полагаю, что это вероятно связано с проблемой курицы/яйца, но я точно не знаю, почему, и что с этим делать. Я предполагаю, что установка PARTIAL_STATE_SAVING на false может помочь, но хотелось бы избежать последствий памяти.
---- ОРИГИНАЛЬНЫЙ ВОПРОС ----
В принципе, у меня есть страница, которая условно отображает разделы, используя <h:panelGroup rendered="#{modeXXX}">
, обернутую вокруг <ui:include src="pageXXX.xhtml" />
(за этот ответ). Проблема заключается в том, что если один из файлов pageXXX.xhtml имеет <ui:repeat>
внутри него, он, похоже, обрабатывается, даже если содержащая <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 <ui:include></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>
Как вы можете видеть, эта страница будет условно включать либо страницу modeOne, либо страницу modeTwo, основанную на значении в testBean bean. Тогда у вас есть 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'm ready. Take me to </span>
<h:commandLink action="#{testBean.setModeTwo}">mode two.</h:commandLink>
</ui:composition>
</html>
Что в моем реальном приложении было бы страницей, которая настраивает вещи, необходимые для режима. После настройки действие на этой странице направит вас в режимTwo:
<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 в основном представляет детали страницы modeOne в ui: repeat, поскольку фактическая информация содержится в коллекции. Основной управляемый 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;
}
}
}
<ui:repeat>
не проверяет атрибут rendered
(он фактически отсутствует) и его родители, когда представление должно быть отображено. Вместо этого используйте Tomahawk <t:dataList>
.