JSF SelectOneMenu 在另一个字段上的验证错误已修复并重新提交表单后显示错误值
JSF SelectOneMenu showing wrong value after validation error on another field was fixed and form re-submitted
我 运行 遇到一个非常奇怪的问题,即 selectOneMenu jsf 控件在出现验证错误后将错误值显示为所选值(即使模型已更新为正确值)之前(不是当前)提交的完全不同的字段。
如何复制(最低代码如下):
- 加载表单并将 "Required text field" 文本框留空。
- 在几个下拉列表中将一些值设置为活动和非活动
以下。
- 提交表格。将出现有关的错误消息
"Required text field" 为空,您设置的下拉菜单将
仍然具有您给他们的相同价值(预期)。的打印输出
模型所具有的仍将显示旧值而不是值
您选择(预期)。
- 将一些文字放入“必填文字”
字段”并提交表单。
- 错误消息消失并且
表格得到 "saved"。模型旁边的打印输出
下拉菜单具有您设置的值(预期),但是,下拉菜单
自己不再有你选择的值作为选择
选项(未预期)。
示例代码:
<?xml version='1.0' encoding='UTF-8' ?>
<!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:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form id="myForm">
<div>
<h:outputLabel value="Required text field" for="reqField" />
<h:inputText value="#{myBeanController.requiredField}" id="reqField" label="Required Text Field" required="true" />
</div>
<div>
<h:outputLabel value="some objects" />
<ui:repeat value="#{myBeanController.bunchOfObjects}" var="obj">
<div>
value on obj model: #{obj}
<h:selectOneMenu value="#{obj.type}">
<f:selectItems value="#{myBeanController.availableRequiredTypeOptions}" />
</h:selectOneMenu>
</div>
</ui:repeat>
</div>
<div>
<h:commandButton value="Submit" action="#{myBeanController.save}" />
</div>
</h:form>
</h:body>
</html>
控制器 bean:
package com.mycompany.reproducefieldnotsavingaftervalidation;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import org.apache.commons.lang3.text.WordUtils;
@ManagedBean
@SessionScoped
public class MyBeanController {
private String requiredField;
private Collection<MyDomainClass> bunchOfObjects;
@PostConstruct
public void init(){
// create some sample objects
bunchOfObjects = new LinkedList<>();
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.ACTIVE));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.INACTIVE));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
}
public void save() {
System.out.println("SAVING FORM");
System.out.println("requiredField: " + getRequiredField());
System.out.println("Bunch of objects: ");
for(MyDomainClass obj : getBunchOfObjects()) {
System.out.println("/tObj: " + obj);
}
}
public Map<String, String> getAvailableRequiredTypeOptions() {
Map<String, String> options = new TreeMap<>();
for(MyDomainClass.PossibleTypes type : MyDomainClass.PossibleTypes.values()) {
// make the text of the option pretty by removing all caps and replacing underscores with space
options.put(WordUtils.capitalizeFully(type.name(), new char[]{'_'}).replaceAll("_", " "), type.name());
}
return options;
}
public String getRequiredField() {
return requiredField;
}
public void setRequiredField(String requiredField) {
this.requiredField = requiredField;
}
public Collection<MyDomainClass> getBunchOfObjects() {
return bunchOfObjects;
}
public void setBunchOfObjects(Collection<MyDomainClass> bunchOfObjects) {
this.bunchOfObjects = bunchOfObjects;
}
}
型号:
package com.mycompany.reproducefieldnotsavingaftervalidation;
import java.util.Objects;
public class MyDomainClass {
private String type;
public MyDomainClass() { }
public MyDomainClass(PossibleTypes type) {
this.type = type.name();
}
public MyDomainClass(String type) {
this.type = type;
}
public static enum PossibleTypes {
UNKNOWN, ACTIVE, INACTIVE
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
return "MyDomainClass{" + "type=" + type + '}';
}
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.getType());
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof MyDomainClass)) {
return false;
}
final MyDomainClass other = (MyDomainClass) obj;
if (!Objects.equals(this.getType(), other.getType())) {
return false;
}
return true;
}
}
切换
<ui:repeat>
到
<c:forEach>
问题似乎已解决。我不知道为什么 ui:repeat 只在先前验证失败后的保存中导致问题,而不是在第一次有效保存时导致问题(如果有人知道这个问题的答案,请 post 它,我会有兴趣知道)。根据一些研究,ui:repeat 发生在与 f:selectItem 或 f:selectItem 不同的阶段,但我希望每次保存时都会发生此问题,但事实并非如此。
无论如何,如果您看到类似的东西并且您的选择在 ui:repeat 内,请尝试将其切换为 c:forEach 以查看是否可以解决问题。
我 运行 遇到一个非常奇怪的问题,即 selectOneMenu jsf 控件在出现验证错误后将错误值显示为所选值(即使模型已更新为正确值)之前(不是当前)提交的完全不同的字段。
如何复制(最低代码如下):
- 加载表单并将 "Required text field" 文本框留空。
- 在几个下拉列表中将一些值设置为活动和非活动 以下。
- 提交表格。将出现有关的错误消息 "Required text field" 为空,您设置的下拉菜单将 仍然具有您给他们的相同价值(预期)。的打印输出 模型所具有的仍将显示旧值而不是值 您选择(预期)。
- 将一些文字放入“必填文字” 字段”并提交表单。
- 错误消息消失并且 表格得到 "saved"。模型旁边的打印输出 下拉菜单具有您设置的值(预期),但是,下拉菜单 自己不再有你选择的值作为选择 选项(未预期)。
示例代码:
<?xml version='1.0' encoding='UTF-8' ?>
<!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:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form id="myForm">
<div>
<h:outputLabel value="Required text field" for="reqField" />
<h:inputText value="#{myBeanController.requiredField}" id="reqField" label="Required Text Field" required="true" />
</div>
<div>
<h:outputLabel value="some objects" />
<ui:repeat value="#{myBeanController.bunchOfObjects}" var="obj">
<div>
value on obj model: #{obj}
<h:selectOneMenu value="#{obj.type}">
<f:selectItems value="#{myBeanController.availableRequiredTypeOptions}" />
</h:selectOneMenu>
</div>
</ui:repeat>
</div>
<div>
<h:commandButton value="Submit" action="#{myBeanController.save}" />
</div>
</h:form>
</h:body>
</html>
控制器 bean:
package com.mycompany.reproducefieldnotsavingaftervalidation;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import org.apache.commons.lang3.text.WordUtils;
@ManagedBean
@SessionScoped
public class MyBeanController {
private String requiredField;
private Collection<MyDomainClass> bunchOfObjects;
@PostConstruct
public void init(){
// create some sample objects
bunchOfObjects = new LinkedList<>();
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.ACTIVE));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.INACTIVE));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
}
public void save() {
System.out.println("SAVING FORM");
System.out.println("requiredField: " + getRequiredField());
System.out.println("Bunch of objects: ");
for(MyDomainClass obj : getBunchOfObjects()) {
System.out.println("/tObj: " + obj);
}
}
public Map<String, String> getAvailableRequiredTypeOptions() {
Map<String, String> options = new TreeMap<>();
for(MyDomainClass.PossibleTypes type : MyDomainClass.PossibleTypes.values()) {
// make the text of the option pretty by removing all caps and replacing underscores with space
options.put(WordUtils.capitalizeFully(type.name(), new char[]{'_'}).replaceAll("_", " "), type.name());
}
return options;
}
public String getRequiredField() {
return requiredField;
}
public void setRequiredField(String requiredField) {
this.requiredField = requiredField;
}
public Collection<MyDomainClass> getBunchOfObjects() {
return bunchOfObjects;
}
public void setBunchOfObjects(Collection<MyDomainClass> bunchOfObjects) {
this.bunchOfObjects = bunchOfObjects;
}
}
型号:
package com.mycompany.reproducefieldnotsavingaftervalidation;
import java.util.Objects;
public class MyDomainClass {
private String type;
public MyDomainClass() { }
public MyDomainClass(PossibleTypes type) {
this.type = type.name();
}
public MyDomainClass(String type) {
this.type = type;
}
public static enum PossibleTypes {
UNKNOWN, ACTIVE, INACTIVE
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
return "MyDomainClass{" + "type=" + type + '}';
}
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.getType());
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof MyDomainClass)) {
return false;
}
final MyDomainClass other = (MyDomainClass) obj;
if (!Objects.equals(this.getType(), other.getType())) {
return false;
}
return true;
}
}
切换
<ui:repeat>
到
<c:forEach>
问题似乎已解决。我不知道为什么 ui:repeat 只在先前验证失败后的保存中导致问题,而不是在第一次有效保存时导致问题(如果有人知道这个问题的答案,请 post 它,我会有兴趣知道)。根据一些研究,ui:repeat 发生在与 f:selectItem 或 f:selectItem 不同的阶段,但我希望每次保存时都会发生此问题,但事实并非如此。
无论如何,如果您看到类似的东西并且您的选择在 ui:repeat 内,请尝试将其切换为 c:forEach 以查看是否可以解决问题。