h:selectOneRadio 嵌套 ui:repeat 元素中的分组问题
h:selectOneRadio group problem within nested ui:repeat elements
我目前正在尝试实现一个动态调查页面,其中包含用户定义的页面、部分和问题(大型应用程序的一部分)。
用户可以定义不同的问题类型,这将呈现不同的组件(radio、textarea、textfield、selectionlist,...)。
此应用目前部署在 Wildfly 16 / Java 8 / JSF 2.3 / Servlet 4.
由于用户可能会为单选按钮定义一组特定的值和关联的图像,因此我需要自定义单选按钮输出。所以我的选择是使用 selectoneradio
中可用的新 group
属性。
在多级 ui:repeat
.
中使用时,使用此新属性会导致不稳定的行为
为了说明问题,简化并创建了以下示例。
Class
package test;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.annotation.PostConstruct;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
@Named("test")
@ViewScoped
public class Test implements Serializable
{
private static final long serialVersionUID = 1L;
private static Map<String, Map<String, String>> questionMap = new TreeMap<>();
private static Map<String, String> fixedValuesMap = new TreeMap<>();
private Map<String, String> answerMap = new TreeMap<>();
private List<String> sections = Arrays.asList("Section_1", "Section_2", "Section_3", "Section_4");
static
{
fixedValuesMap.put("1", "Value 1");
fixedValuesMap.put("2", "Value 2");
fixedValuesMap.put("3", "Value 3");
fixedValuesMap.put("4", "Value 4");
fixedValuesMap.put("5", "Value 5");
Map<String, String> sec1questions = new TreeMap<>();
sec1questions.put("1", "Question 1");
sec1questions.put("2", "Question 2");
sec1questions.put("3", "Question 3");
questionMap.put("Section_1", sec1questions);
Map<String, String> sec2questions = new TreeMap<>();
sec2questions.put("4", "Question 4");
questionMap.put("Section_2", sec2questions);
Map<String, String> sec3questions = new TreeMap<>();
sec3questions.put("5", "Question 5");
questionMap.put("Section_3", sec3questions);
Map<String, String> sec4questions = new TreeMap<>();
sec4questions.put("6", "Question 6");
questionMap.put("Section_4", sec4questions);
}
public Test()
{
}
@PostConstruct
private void init()
{
answerMap.put("1", null);
answerMap.put("2", null);
answerMap.put("3", null);
answerMap.put("4", null);
answerMap.put("5", null);
answerMap.put("6", null);
}
public String getQuestionType(String index)
{
switch(index)
{
case "1":
return "RADIO_BUTTON";
case "2":
return "RADIO_BUTTON";
case "3":
return "RADIO_BUTTON";
case "4":
return "TEXT_AREA";
case "5":
return "RADIO_BUTTON";
case "6":
return "FREE_TEXT";
default:
return "FREE_TEXT";
}
}
public Map<String, String> getQuestions(String section)
{
return questionMap.get(section);
}
public Map<String, String> getAnswerMap()
{
return answerMap;
}
public Map<String, String> getFixedValues()
{
return fixedValuesMap;
}
public List<String> getSections()
{
return sections;
}
public void changeRadio(String questionKey, String questionValue)
{
answerMap.put(questionKey, questionValue);
}
public String submit()
{
for(Map.Entry<String, String> entry : answerMap.entrySet())
System.out.println("ENTRY: " + entry.getKey() + " - " + entry.getValue());
return null;
}
}
XHTML
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:a="http://xmlns.jcp.org/jsf/passthrough">
<h:head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
<meta name="description" content="My Test"/>
<link rel="stylesheet" href="#{request.contextPath}/resources/bootstrap/css/bootstrap.min.css" />
<title>My Test</title>
</h:head>
<h:body>
<div class="container-fluid">
<div class="row">
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">UI Repeat Test</h1>
</div>
<h:form role="form" prependId="false">
<div class="row mx-2">
<ui:repeat var="sec" value="#{test.sections}">
<div class="col-12">
<ui:repeat var="question" value="#{test.getQuestions(sec)}">
<div>
<div class="col-12">
<div class="form-group">
<label for="answer">#{question.key} - #{question.value}</label>
<div class="col-12">
<h:panelGroup rendered="#{test.getQuestionType(question.key) eq 'FREE_TEXT'}" layout="block">
<h:inputText value="#{test.answerMap[question.key]}" styleClass="form-control"/>
</h:panelGroup>
<h:panelGroup rendered="#{test.getQuestionType(question.key) eq 'TEXT_AREA'}" layout="block">
<h:inputTextarea value="#{test.answerMap[question.key]}" styleClass="form-control" rows="2" />
</h:panelGroup>
<h:panelGroup rendered="#{test.getQuestionType(question.key) eq 'RADIO_BUTTON'}" layout="block">
<table class="radio-label checkbox">
<tbody>
<tr>
<ui:repeat var="item" value="#{test.fixedValues.entrySet()}">
<td class="text-center grid-margin">
<h:selectOneRadio group="#{question.key}" value="#{test.answerMap[question.key]}" layout="lineDirection" styleClass="checkbox" >
<f:selectItem itemValue="#{item.key}" itemLabel="#{item.value}" />
</h:selectOneRadio>
</td>
</ui:repeat>
</tr>
</tbody>
</table>
</h:panelGroup>
</div>
</div>
</div>
</div>
</ui:repeat>
</div>
</ui:repeat>
</div>
<div class="row mx-2 my-2">
<div class="col-12">
<h:commandButton value="Test Me" type="submit" action="#{test.submit}" styleClass="btn btn-primary" />
</div>
</div>
</h:form>
</main>
</div>
</div>
<script src="#{request.contextPath}/resources/jquery/jquery.slim.min.js"></script>
<script src="#{request.contextPath}/resources/bootstrap/js/bootstrap.bundle.min.js"></script>
</h:body>
</html>
提交表单时,仅存储最后一个单选按钮和文本字段值。
如果我替换 selectItems ui:repeat
<table class="radio-label checkbox">
<tbody>
<tr>
<ui:repeat var="item" value="#{test.fixedValues.entrySet()}">
<td class="text-center grid-margin">
<h:selectOneRadio group="#{question.key}" value="#{test.answerMap[question.key]}" layout="lineDirection" styleClass="checkbox" >
<f:selectItem itemValue="#{item.key}" itemLabel="#{item.value}" />
</h:selectOneRadio>
</td>
</ui:repeat>
</tr>
</tbody>
</table>
和
<h:selectOneRadio value="#{test.answerMap[question.key]}" layout="lineDirection" >
<f:selectItems value="#{test.fixedValues.entrySet()}" var="item" itemLabel="#{item.key}" itemValue="#{item.value}" />
</h:selectOneRadio>
一切正常。
我真的需要使用第一个选项,所以我可以在单选按钮值中添加一些图像。
我错过了什么吗?
你好像发现了一个问题。我尝试将示例简化如下:
我的托管Bean:
package my.pkg;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
@Named
@RequestScoped
public class MyBean {
private List<String> items;
private List<Number> numbers;
private Map<String, Object> mapVal;
@PostConstruct
public void init() {
mapVal = new LinkedHashMap<>();
items = new ArrayList<>();
numbers = new ArrayList<>();
for (int i = 0; i < 3; i++) {
items.add("group_" + i);
numbers.add(i);
}
}
// getters/setters ...
}
范例:
<!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://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<h:head />
<h:body>
<h:form id="frm1">
<ui:repeat var="grp" value="#{myBean.items}">
<ui:repeat var="num" value="#{myBean.numbers}" varStatus="stat">
<h:selectOneRadio id="radioButton" value="#{myBean.mapVal[grp]}"
group="myGroup_#{grp}">
<f:selectItem itemValue="#{num}" itemLabel="#{grp}: #{num}" />
</h:selectOneRadio>
<br />
</ui:repeat>
<hr />
</ui:repeat>
<h:commandButton value="submit" />
<h:outputText value="myBean.mapVal: #{myBean.mapVal}" />
</h:form>
</h:body>
</html>
用户界面示例输入:
提交后我希望输出为:
myBean.mapVal: {group_0=0, group_1=1, group_2=2}
但它是:
myBean.mapVal: {group_2=2}
第 0 组和第 1 组的单选按钮失去选择。
对于某些输入,我得到了预期的结果:
myBean.mapVal: {group_0=0, group_1=0, group_2=0}
myBean.mapVal: {group_0=1, group_1=1, group_2=1}
myBean.mapVal: {group_0=2, group_1=2, group_2=2}
...使用 Mojarra 2.3.9。
对于 MyFaces 2.3.4,我在上面的屏幕截图中得到了预期的选择结果:
{group_0=0, group_1=1, group_2=2}
但其他选择失败:
1)
expected: {group_0=0, group_1=0, group_2=0}
but is: {group_2=0}
2)
expected: {group_0=0, group_1=0, group_2=2}
but is: {group_1=0, group_2=2}
3)
expected: {group_0=1, group_1=1, group_2=0}
but is: {group_1=1, group_2=0}
在我看来,我们应该报告 Mojarra 和 MyFaces(如果还没有)的问题。
解决方法
如果我替换
<ui:repeat var="grp" value="#{myBean.items}">
来自
<c:forEach var="grp" items="#{myBean.items}">
我得到了 Mojarra 和 MyFaces 的预期结果。
正如@Selaron 建议的那样,我在 eclipse-ee4j/mojarra
下创建了一个问题
感谢您的帮助!
我目前正在尝试实现一个动态调查页面,其中包含用户定义的页面、部分和问题(大型应用程序的一部分)。 用户可以定义不同的问题类型,这将呈现不同的组件(radio、textarea、textfield、selectionlist,...)。 此应用目前部署在 Wildfly 16 / Java 8 / JSF 2.3 / Servlet 4.
由于用户可能会为单选按钮定义一组特定的值和关联的图像,因此我需要自定义单选按钮输出。所以我的选择是使用 selectoneradio
中可用的新 group
属性。
在多级 ui:repeat
.
为了说明问题,简化并创建了以下示例。
Class
package test;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.annotation.PostConstruct;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
@Named("test")
@ViewScoped
public class Test implements Serializable
{
private static final long serialVersionUID = 1L;
private static Map<String, Map<String, String>> questionMap = new TreeMap<>();
private static Map<String, String> fixedValuesMap = new TreeMap<>();
private Map<String, String> answerMap = new TreeMap<>();
private List<String> sections = Arrays.asList("Section_1", "Section_2", "Section_3", "Section_4");
static
{
fixedValuesMap.put("1", "Value 1");
fixedValuesMap.put("2", "Value 2");
fixedValuesMap.put("3", "Value 3");
fixedValuesMap.put("4", "Value 4");
fixedValuesMap.put("5", "Value 5");
Map<String, String> sec1questions = new TreeMap<>();
sec1questions.put("1", "Question 1");
sec1questions.put("2", "Question 2");
sec1questions.put("3", "Question 3");
questionMap.put("Section_1", sec1questions);
Map<String, String> sec2questions = new TreeMap<>();
sec2questions.put("4", "Question 4");
questionMap.put("Section_2", sec2questions);
Map<String, String> sec3questions = new TreeMap<>();
sec3questions.put("5", "Question 5");
questionMap.put("Section_3", sec3questions);
Map<String, String> sec4questions = new TreeMap<>();
sec4questions.put("6", "Question 6");
questionMap.put("Section_4", sec4questions);
}
public Test()
{
}
@PostConstruct
private void init()
{
answerMap.put("1", null);
answerMap.put("2", null);
answerMap.put("3", null);
answerMap.put("4", null);
answerMap.put("5", null);
answerMap.put("6", null);
}
public String getQuestionType(String index)
{
switch(index)
{
case "1":
return "RADIO_BUTTON";
case "2":
return "RADIO_BUTTON";
case "3":
return "RADIO_BUTTON";
case "4":
return "TEXT_AREA";
case "5":
return "RADIO_BUTTON";
case "6":
return "FREE_TEXT";
default:
return "FREE_TEXT";
}
}
public Map<String, String> getQuestions(String section)
{
return questionMap.get(section);
}
public Map<String, String> getAnswerMap()
{
return answerMap;
}
public Map<String, String> getFixedValues()
{
return fixedValuesMap;
}
public List<String> getSections()
{
return sections;
}
public void changeRadio(String questionKey, String questionValue)
{
answerMap.put(questionKey, questionValue);
}
public String submit()
{
for(Map.Entry<String, String> entry : answerMap.entrySet())
System.out.println("ENTRY: " + entry.getKey() + " - " + entry.getValue());
return null;
}
}
XHTML
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:a="http://xmlns.jcp.org/jsf/passthrough">
<h:head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
<meta name="description" content="My Test"/>
<link rel="stylesheet" href="#{request.contextPath}/resources/bootstrap/css/bootstrap.min.css" />
<title>My Test</title>
</h:head>
<h:body>
<div class="container-fluid">
<div class="row">
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">UI Repeat Test</h1>
</div>
<h:form role="form" prependId="false">
<div class="row mx-2">
<ui:repeat var="sec" value="#{test.sections}">
<div class="col-12">
<ui:repeat var="question" value="#{test.getQuestions(sec)}">
<div>
<div class="col-12">
<div class="form-group">
<label for="answer">#{question.key} - #{question.value}</label>
<div class="col-12">
<h:panelGroup rendered="#{test.getQuestionType(question.key) eq 'FREE_TEXT'}" layout="block">
<h:inputText value="#{test.answerMap[question.key]}" styleClass="form-control"/>
</h:panelGroup>
<h:panelGroup rendered="#{test.getQuestionType(question.key) eq 'TEXT_AREA'}" layout="block">
<h:inputTextarea value="#{test.answerMap[question.key]}" styleClass="form-control" rows="2" />
</h:panelGroup>
<h:panelGroup rendered="#{test.getQuestionType(question.key) eq 'RADIO_BUTTON'}" layout="block">
<table class="radio-label checkbox">
<tbody>
<tr>
<ui:repeat var="item" value="#{test.fixedValues.entrySet()}">
<td class="text-center grid-margin">
<h:selectOneRadio group="#{question.key}" value="#{test.answerMap[question.key]}" layout="lineDirection" styleClass="checkbox" >
<f:selectItem itemValue="#{item.key}" itemLabel="#{item.value}" />
</h:selectOneRadio>
</td>
</ui:repeat>
</tr>
</tbody>
</table>
</h:panelGroup>
</div>
</div>
</div>
</div>
</ui:repeat>
</div>
</ui:repeat>
</div>
<div class="row mx-2 my-2">
<div class="col-12">
<h:commandButton value="Test Me" type="submit" action="#{test.submit}" styleClass="btn btn-primary" />
</div>
</div>
</h:form>
</main>
</div>
</div>
<script src="#{request.contextPath}/resources/jquery/jquery.slim.min.js"></script>
<script src="#{request.contextPath}/resources/bootstrap/js/bootstrap.bundle.min.js"></script>
</h:body>
</html>
提交表单时,仅存储最后一个单选按钮和文本字段值。 如果我替换 selectItems ui:repeat
<table class="radio-label checkbox">
<tbody>
<tr>
<ui:repeat var="item" value="#{test.fixedValues.entrySet()}">
<td class="text-center grid-margin">
<h:selectOneRadio group="#{question.key}" value="#{test.answerMap[question.key]}" layout="lineDirection" styleClass="checkbox" >
<f:selectItem itemValue="#{item.key}" itemLabel="#{item.value}" />
</h:selectOneRadio>
</td>
</ui:repeat>
</tr>
</tbody>
</table>
和
<h:selectOneRadio value="#{test.answerMap[question.key]}" layout="lineDirection" >
<f:selectItems value="#{test.fixedValues.entrySet()}" var="item" itemLabel="#{item.key}" itemValue="#{item.value}" />
</h:selectOneRadio>
一切正常。
我真的需要使用第一个选项,所以我可以在单选按钮值中添加一些图像。
我错过了什么吗?
你好像发现了一个问题。我尝试将示例简化如下:
我的托管Bean:
package my.pkg;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
@Named
@RequestScoped
public class MyBean {
private List<String> items;
private List<Number> numbers;
private Map<String, Object> mapVal;
@PostConstruct
public void init() {
mapVal = new LinkedHashMap<>();
items = new ArrayList<>();
numbers = new ArrayList<>();
for (int i = 0; i < 3; i++) {
items.add("group_" + i);
numbers.add(i);
}
}
// getters/setters ...
}
范例:
<!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://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<h:head />
<h:body>
<h:form id="frm1">
<ui:repeat var="grp" value="#{myBean.items}">
<ui:repeat var="num" value="#{myBean.numbers}" varStatus="stat">
<h:selectOneRadio id="radioButton" value="#{myBean.mapVal[grp]}"
group="myGroup_#{grp}">
<f:selectItem itemValue="#{num}" itemLabel="#{grp}: #{num}" />
</h:selectOneRadio>
<br />
</ui:repeat>
<hr />
</ui:repeat>
<h:commandButton value="submit" />
<h:outputText value="myBean.mapVal: #{myBean.mapVal}" />
</h:form>
</h:body>
</html>
用户界面示例输入:
提交后我希望输出为:
myBean.mapVal: {group_0=0, group_1=1, group_2=2}
但它是:
myBean.mapVal: {group_2=2}
第 0 组和第 1 组的单选按钮失去选择。
对于某些输入,我得到了预期的结果:
myBean.mapVal: {group_0=0, group_1=0, group_2=0}
myBean.mapVal: {group_0=1, group_1=1, group_2=1}
myBean.mapVal: {group_0=2, group_1=2, group_2=2}
...使用 Mojarra 2.3.9。
对于 MyFaces 2.3.4,我在上面的屏幕截图中得到了预期的选择结果:
{group_0=0, group_1=1, group_2=2}
但其他选择失败:
1)
expected: {group_0=0, group_1=0, group_2=0}
but is: {group_2=0}
2)
expected: {group_0=0, group_1=0, group_2=2}
but is: {group_1=0, group_2=2}
3)
expected: {group_0=1, group_1=1, group_2=0}
but is: {group_1=1, group_2=0}
在我看来,我们应该报告 Mojarra 和 MyFaces(如果还没有)的问题。
解决方法
如果我替换
<ui:repeat var="grp" value="#{myBean.items}">
来自
<c:forEach var="grp" items="#{myBean.items}">
我得到了 Mojarra 和 MyFaces 的预期结果。
正如@Selaron 建议的那样,我在 eclipse-ee4j/mojarra
下创建了一个问题感谢您的帮助!