JSF SelectManyListbox with noSelectionOption="true" - Validation Error: Value is not valid
JSF SelectManyListbox with noSelectionOption="true" - Validation Error: Value is not valid
我在使用 h:selectManyListbox
时遇到问题,当项目填充了 POJO 并且 noSelectionOption
为真时(对于 h:selectManyListbox
以枚举作为项目,它按我预期的方式工作) .
豆子
@Named
@ViewScoped
public class MyBean implements Serializable {
private static final long serialVersionUID = 1L;
private List<BaseDTO> availableItems = null;
private String[] selectedItems = null;
@PostConstruct
private void initialize() {
loadAvailableItems();
}
private void loadAvailableItems() {
availableItems = Arrays.asList(new BaseDTO("entityId", "entityDescription"), new BaseDTO(...), ...);
}
public List<BaseDTO> getAvailableItems() {
return availableItems;
}
public String[] getSelectedItems() {
return selectedItems;
}
public void setSelectedItems(String[] selectedItems) {
this.selectedItems = selectedItems;
}
}
BaseDTO
public class BaseDTO {
private String id;
private String description;
public BaseDTO(String id, String description) {
this.id = id;
this.description = description;
}
public String getId() {
return id;
}
public String getDescription() {
return description;
}
@Override
public String toString() {
return id;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BaseDTO other = (BaseDTO) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
}
XHTML
<h:selectManyListbox value="#{myBean.selectedItems}" hideNoSelectionOption="false" size="4">
<f:selectItem itemValue="#{null}" itemLabel="--" noSelectionOption="true" />
<f:selectItems value="#{myBean.availableItems}" var="entry" itemValue="#{entry.id}" itemLabel="#{entry.description}" />
</h:selectManyListbox>
当我尝试提交页面时,我总是得到 Validation Error: Value is not valid
。
如果我删除 hideNoSelectionOption
和相应的 <f:selectItem itemValue="#{null}" itemLabel="--" noSelectionOption="true" />
一切正常,但是我真的很想在我的列表中添加这个 noSelectionOption
。
我尝试使用 OmniFaces SelectItemsConverter 甚至创建我自己的自定义转换器,但没有成功。无论我尝试什么,我都无法克服这个验证错误。
同时我发现了一个不太好的解决方法:
如果我的 availableItems
变量是 Map<String, String>
而不是 List
:
private Map<String, String> availableItems = null;
如果我向地图添加一个空条目:
private void loadAvailableItems() {
List<BaseDTO> dtoList = Arrays.asList(new BaseDTO("entityId", "entityDescription"));
availableItems = dtoList.stream().collect(Collectors.toMap(BaseDTO::getId, BaseDTO::getDescription));
availableItems.put(null, "--");
}
然后,一切都按预期进行,除了 noSelectionOption
没有在页面上预先选择。
这是预期的组件行为,还是我遗漏了什么?
在此先感谢您的帮助!
看起来它正在尝试验证该值,但它不能。您是否尝试过使用自定义验证器?
首先,noSelectionOption
/hideNoSelectionOption
属性对在这里被误解了。请删除它们。它在您的上下文中完全没有用。为了更好地理解他们的初衷,请前往Best way to add a "nothing selected" option to a selectOneMenu in JSF的答案,总结如下:
The primary purpose of this attribute pair is to prevent the web site user from being able to re-select the "no selection option" when the component has already a non-null value selected.
在您的特定情况下,您有一个多 select 列表框。首先,在这样的用户界面元素中使用“nothing selected”选项毫无意义。您只需要 deselect 一切,以达到“无 selected”状态。这在例如单个 select 下拉列表中是不可能的,因为您不能首先 deselect selected 选项。因此,此类用户界面元素需要“无 selected”选项。但是,再说一遍,多 select 列表框不需要这样做。然而,我确实明白,拥有一个可操作的元素是很有用的,它可以自动取消 select 列表框中的所有内容。这可以通过列表框附近某处的 link 或按钮完成。
无论如何,我已经能够在 Mojarra 2.3.17 中重现所描述的问题。根本问题是“空字符串提交值”不再由空字符串数组表示,而是由具有单个项目的字符串数组表示,一个空字符串。因此,所有与“空字符串提交值”相关的检查随后都失败了。我不认为这是 JSF 本身的错误,而只是意外使用多 select 组件的情况。
您可以通过在呈现响应阶段(第 6 阶段)以外的所有阶段显式禁用该项目来解决所有问题。它将 select 可用,但会自动从 select 编辑的项目中删除,作为针对篡改请求的内置措施。这样“提交的空字符串值”将是预期的空数组。
<h:selectManyListbox value="#{myBean.selectedItems}" size="4">
<f:selectItem itemValue="#{null}" itemLabel="--" itemDisabled="#{facesContext.currentPhaseId.ordinal ne 6}" />
<f:selectItems value="#{myBean.availableItems}" var="entry" itemValue="#{entry.id}" itemLabel="#{entry.description}" />
</h:selectManyListbox>
请注意,这无法通过自定义转换器或验证器解决。 JSF 不允许自定义转换器返回 null
,并且此特定的“值无效”验证由内置验证器针对不能 replaced/disabled 的篡改请求完成。我们最好的选择可能是 change/respecify noSelectionOption="true"
的行为,因为这确实经常被误解。它可能应该在内部以与禁用项目相同的方式处理。
我在使用 h:selectManyListbox
时遇到问题,当项目填充了 POJO 并且 noSelectionOption
为真时(对于 h:selectManyListbox
以枚举作为项目,它按我预期的方式工作) .
豆子
@Named
@ViewScoped
public class MyBean implements Serializable {
private static final long serialVersionUID = 1L;
private List<BaseDTO> availableItems = null;
private String[] selectedItems = null;
@PostConstruct
private void initialize() {
loadAvailableItems();
}
private void loadAvailableItems() {
availableItems = Arrays.asList(new BaseDTO("entityId", "entityDescription"), new BaseDTO(...), ...);
}
public List<BaseDTO> getAvailableItems() {
return availableItems;
}
public String[] getSelectedItems() {
return selectedItems;
}
public void setSelectedItems(String[] selectedItems) {
this.selectedItems = selectedItems;
}
}
BaseDTO
public class BaseDTO {
private String id;
private String description;
public BaseDTO(String id, String description) {
this.id = id;
this.description = description;
}
public String getId() {
return id;
}
public String getDescription() {
return description;
}
@Override
public String toString() {
return id;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BaseDTO other = (BaseDTO) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
}
XHTML
<h:selectManyListbox value="#{myBean.selectedItems}" hideNoSelectionOption="false" size="4">
<f:selectItem itemValue="#{null}" itemLabel="--" noSelectionOption="true" />
<f:selectItems value="#{myBean.availableItems}" var="entry" itemValue="#{entry.id}" itemLabel="#{entry.description}" />
</h:selectManyListbox>
当我尝试提交页面时,我总是得到 Validation Error: Value is not valid
。
如果我删除 hideNoSelectionOption
和相应的 <f:selectItem itemValue="#{null}" itemLabel="--" noSelectionOption="true" />
一切正常,但是我真的很想在我的列表中添加这个 noSelectionOption
。
我尝试使用 OmniFaces SelectItemsConverter 甚至创建我自己的自定义转换器,但没有成功。无论我尝试什么,我都无法克服这个验证错误。
同时我发现了一个不太好的解决方法:
如果我的 availableItems
变量是 Map<String, String>
而不是 List
:
private Map<String, String> availableItems = null;
如果我向地图添加一个空条目:
private void loadAvailableItems() {
List<BaseDTO> dtoList = Arrays.asList(new BaseDTO("entityId", "entityDescription"));
availableItems = dtoList.stream().collect(Collectors.toMap(BaseDTO::getId, BaseDTO::getDescription));
availableItems.put(null, "--");
}
然后,一切都按预期进行,除了 noSelectionOption
没有在页面上预先选择。
这是预期的组件行为,还是我遗漏了什么?
在此先感谢您的帮助!
看起来它正在尝试验证该值,但它不能。您是否尝试过使用自定义验证器?
首先,noSelectionOption
/hideNoSelectionOption
属性对在这里被误解了。请删除它们。它在您的上下文中完全没有用。为了更好地理解他们的初衷,请前往Best way to add a "nothing selected" option to a selectOneMenu in JSF的答案,总结如下:
The primary purpose of this attribute pair is to prevent the web site user from being able to re-select the "no selection option" when the component has already a non-null value selected.
在您的特定情况下,您有一个多 select 列表框。首先,在这样的用户界面元素中使用“nothing selected”选项毫无意义。您只需要 deselect 一切,以达到“无 selected”状态。这在例如单个 select 下拉列表中是不可能的,因为您不能首先 deselect selected 选项。因此,此类用户界面元素需要“无 selected”选项。但是,再说一遍,多 select 列表框不需要这样做。然而,我确实明白,拥有一个可操作的元素是很有用的,它可以自动取消 select 列表框中的所有内容。这可以通过列表框附近某处的 link 或按钮完成。
无论如何,我已经能够在 Mojarra 2.3.17 中重现所描述的问题。根本问题是“空字符串提交值”不再由空字符串数组表示,而是由具有单个项目的字符串数组表示,一个空字符串。因此,所有与“空字符串提交值”相关的检查随后都失败了。我不认为这是 JSF 本身的错误,而只是意外使用多 select 组件的情况。
您可以通过在呈现响应阶段(第 6 阶段)以外的所有阶段显式禁用该项目来解决所有问题。它将 select 可用,但会自动从 select 编辑的项目中删除,作为针对篡改请求的内置措施。这样“提交的空字符串值”将是预期的空数组。
<h:selectManyListbox value="#{myBean.selectedItems}" size="4">
<f:selectItem itemValue="#{null}" itemLabel="--" itemDisabled="#{facesContext.currentPhaseId.ordinal ne 6}" />
<f:selectItems value="#{myBean.availableItems}" var="entry" itemValue="#{entry.id}" itemLabel="#{entry.description}" />
</h:selectManyListbox>
请注意,这无法通过自定义转换器或验证器解决。 JSF 不允许自定义转换器返回 null
,并且此特定的“值无效”验证由内置验证器针对不能 replaced/disabled 的篡改请求完成。我们最好的选择可能是 change/respecify noSelectionOption="true"
的行为,因为这确实经常被误解。它可能应该在内部以与禁用项目相同的方式处理。