Jsf 级联p:selectOneMenu失败,验证错误:值无效

Jsf 级联p:selectOneMenu失败,验证错误:值无效,jsf,primefaces,hashmap,selectonemenu,Jsf,Primefaces,Hashmap,Selectonemenu,我有两个级联p:selectOneMenu组件。第一个显示hashmap中的区域,第二个通过ajax显示所选区域的城市。但是当提交选中城市的表单时,我得到 验证错误:值无效 这里是风景 <p:selectOneMenu id="area" value="#{bean.area}"> <f:selectItem itemLabel="Select Area" itemValue="" /> <f:selectItems value="#{bean.ar

我有两个级联
p:selectOneMenu
组件。第一个显示hashmap中的区域,第二个通过ajax显示所选区域的城市。但是当提交选中城市的表单时,我得到

验证错误:值无效

这里是风景

<p:selectOneMenu id="area" value="#{bean.area}">
    <f:selectItem itemLabel="Select Area" itemValue="" />
    <f:selectItems value="#{bean.areas}" />
    <p:ajax update="city" listener="#{bean.handleAreaChange()}" />
</p:selectOneMenu>
<p:selectOneMenu id="city" value="#{bean.city}">
    <f:selectItem itemLabel="Select City" itemValue="" />
    <f:selectItems value="#{bean.cities}" />
</p:selectOneMenu>  

这是豆子:

private String area;
private String city;
private Map<String, String> areas = new HashMap<String, String>();
private Map<String, String> cities = new HashMap<String, String>();
private Map<String, Map<String, String>> allCities = new HashMap<String, Map<String,String>>();

public void handleAreaChange() {
    if (area != null && !area.isEmpty()) {
        cities = allCities.get(area);
    } else {
        cities = new HashMap<String, String>();
    }
}
私有字符串区域;
私人城市;
私有映射区域=新HashMap();
私有地图城市=新HashMap();
私有地图allCities=newhashmap();
public void handleAreaChange(){
if(area!=null&&!area.isEmpty()){
城市=所有城市。获取(面积);
}否则{
cities=新HashMap();
}
}

这是如何导致的,我如何解决它?

您需要做的第一件事是告诉链接ajax到事件。因此,您可能希望它发生在valueChange事件上,因此您的ajax应该是:

<p:ajax update="tmil2" event="valueChange" listener="#{BeanAreaAndCity.handleCityChange()}"></p:ajax> 

当下拉列表的提交值不包含在可用值列表中时,您将收到此错误。因此,这意味着,与显示表单的HTTP请求相比,在表单提交的HTTP请求期间,可用值的列表发生了不兼容的更改。这反过来通常意味着支持bean是请求范围的,并且可用值的列表没有在其(post)构造函数中预初始化

无论是将bean放入视图范围,还是添加基于请求参数正确预初始化可用值列表的代码,都可以解决您的问题。

正如BalusC所说,
selectItem
的值(标识符)已更改。如果bean的作用域是请求,或者对象已更改服务器端,则可能发生这种情况

我使用的一种常见解决方案是将
selectItem
的值设置为它所表示的对象的id

假设
城市
地区
具有唯一标识符(id),如下所示:

public class City //or Area
{
    private Long id;
    private String name;

    //Rest of class
}
然后,您可以为
城市
地区
引入转换器:

@FacesConverter("cityConverter")
public class CityConverter implements javax.faces.convert.Converter
{
    @Override
    public Object getAsObject(FacesContext ctx, UIComponent cmp, String str)
    {
        //Convert id to Object (City) and return it
    }

    @Override String getAsString(FacesContext ctx, UIComponent, Object obj)
    {
        //Return the id that represents the Object (City) here
        return ((City)obj).getId().toString();
    }
}
在JSF页面中,使用转换器:

<p:selectOneMenu id="area" value="#{bean.area}" converter="areaConverter">
    <f:selectItem itemLabel="Select Area" itemValue="" />
    <f:selectItems value="#{bean.areas}" />
    <p:ajax update="city" listener="#{bean.handleAreaChange()}" />
</p:selectOneMenu>
<p:selectOneMenu id="city" value="#{bean.city}" converter="cityConverter">
    <f:selectItem itemLabel="Select City" itemValue="" />
    <f:selectItems value="#{bean.cities}" />
</p:selectOneMenu>


.

这已经是默认事件。所以这不会改变OP的任何内容。我总是显式地声明事件。我甚至没有意识到它会默认为valueChange(尽管我想这是有道理的)。看来我今天也学到了一些东西。这就是本论坛的魅力所在。+1用于用示例扩展BalusC的答案。