验证错误:值在级联h:selectOneMenu时无效

时间:2012-12-04 15:04:19

标签: validation jsf-2 selectonemenu

我得到了

  

j_idt7:city:验证错误:值无效

切入奶酪,ManagedBean代码:

 //few imports here
 @ManagedBean
 @SessionScoped
 public class CountriesAndCities implements Serializable{
 private List<SelectItem> countries;
 private List<SelectItem> cities;
 private  Map<String,List> m;
 private String selectedCountry;

 public String getSelectedCountry() {
return selectedCountry;
 }

 public void setSelectedCountry(String selectedCountry) {
this.selectedCountry = selectedCountry;
 }

 public CountriesAndCities(){
countries = new ArrayList<SelectItem>();
cities = new ArrayList<SelectItem>();
m = new HashMap<String,List>();
m.put("France", Arrays.asList("paris","marseille"));
m.put("England", Arrays.asList("Munchester","liverpoor"));

 }

 public  List<SelectItem> getCountries(){   
cities.removeAll(cities);
countries.removeAll(countries); 
countries.add(new SelectItem("select country"));
for(Map.Entry<String, List> entry: m.entrySet()){
    countries.add(new SelectItem(entry.getKey()));
    }

return countries;
 }
 public List<SelectItem> getCities(){   
for(Map.Entry<String, List> entry: m.entrySet())
      {if(entry.getKey().toString().equals(selectedCountry)){
        cities.addAll(entry.getValue());
        break;
    }
}
return cities;
 }
 public void checkSelectedCountry(ValueChangeEvent event){
selectedCountry = event.getNewValue().toString();   
 }

这是我的.xhtml的片段:

 <h:selectOneMenu immediate="true" value="#{countriesAndCities.selectedCountry}" 
 onchange="submit()" valueChangeListener="#{countriesAndCities.checkSelectedCountry}">
 <f:selectItems value="#{countriesAndCities.countries}"></f:selectItems>
 </h:selectOneMenu>
 <br/>
 <h:selectOneMenu id="city">
 <f:selectItems value="#{countriesAndCities.cities}"></f:selectItems>
 </h:selectOneMenu>     
 </h:form>

代码完成了应该是的,但是我在第一行得到了上面提到的错误,只有当我点击英格兰并选择国家选择时,我不知道为什么,我在Ajax化代码中编写了相同的任务,并且它运作良好,任何一只手都会感激不尽。

3 个答案:

答案 0 :(得分:3)

如果所选项目的Validation Error: Value is not valid测试未针对列表中的任何可用项目返回equals(),则会引发错误true。因此,它基本上有以下两个原因:

  1. 项目值类型的equals()方法丢失或损坏。
  2. 在提交期间,可用项目列表已无法更改。
  3. 项值类型为String,因此其equals()方法无疑已正确实现(否则会破坏Java世界中的所有内容)。因此原因1可能被划伤。遗留下来的原因2.是的,实际上,由于某些不明原因,你正在清空国家中可用城市的清单。

    public List<SelectItem> getCountries() {   
        cities.removeAll(cities);
        // ...
    }
    

    这段代码没有意义。当要验证所选国家/地区时,也会调用getter。这样,当稍后要验证时,在可用城市列表中找不到所选城市。顺便说一句,你的getCities()也没有任何意义。您正在遍历整个地图而不是仅使用Map#get()

    这一切都只是破解了代码设计。 Getters不应该包含任何业务逻辑。 Getters应该只返回bean属性。那些bean属性应该在(post)构造函数或action(listener)方法之前很久就已经初始化了。

    如何解决这个问题是一个很好的第二个问题。 <h:selectOneMenu valueChangeListener>在这里基本上被滥用了。这是工作的错误工具。您应该使用<f:ajax listener>来代替。但如果您真的坚持滥用valueChangeListener作业,那么您应该按照以下方式解决它,从而将ValueChangeEvent排队到INVOKE_APPLICATION阶段并在该阶段执行工作(好像它是一个(ajax)动作监听器方法):

    private List<String> countries;
    private List<String> cities;
    private Map<String, List<String>> countriesAndCities;
    private String selectedCountry;
    
    @PostConstruct
    public void init() {
        countriesAndCities = new LinkedHashMap<String, List<String>>(); // Note: LinkedHashMap remembers insertion order, HashMap not.
        countriesAndCities.put("France", Arrays.asList("paris","marseille"));
        countriesAndCities.put("England", Arrays.asList("Munchester","liverpoor"));
        countries = new ArrayList<String>(countriesAndCities.keySet());
    }
    
    public void checkSelectedCountry(ValueChangeEvent event) { // I'd rename that method name to loadCities() or something more sensible.
        if (event.getPhaseId() != PhaseId.INVOKE_APPPLICATION) {
            // Move the job to the INVOKE_APPLICATION phase. 
            // The PROCESS_VALIDATIONS phase is the wrong phase for the "action listener"-like job.
            event.setPhaseId(PhaseId.INVOKE_APPLICATION);
            event.queue();
            return;
        }
    
        cities = countriesAndCities.get(selectedCountry);
    }
    
    // Getters and setters. Do not change them! They should just get and set properties. Nothing more!
    
    public List<String> getCountries(){   
        return countries;
    }
    
    public List<String> getCities(){   
        return cities;
    }
    
    public String getSelectedCountry() {
        return selectedCountry;
    }
    
    public void setSelectedCountry(String selectedCountry) {
        this.selectedCountry = selectedCountry;
    }
    

    请注意,当您进行某些表单验证时,这仍会导致潜在问题。更好的解决方案是使用<f:ajax>代替。

    另见:

答案 1 :(得分:1)

摆脱immediate="true"onchange="submit()"

添加

<f:ajax render="city"/>

在您的第一个<h:selectOneMenu

内 像这样

<h:selectOneMenu  value="#{countriesAndCities.selectedCountry}" valueChangeListener="#{countriesAndCities.checkSelectedCountry}">
     <f:selectItems value="#{countriesAndCities.countries}"></f:selectItems>
     <f:ajax render="city"/>
</h:selectOneMenu>

并阅读此Learning JSF2: Ajax in JSF – using f:ajax tag

答案 2 :(得分:1)

我建议你在吸气剂中减少处理(阅读:无处理)。如果出于任何原因,绑定到组件的数据源的内容在请求中被修改,则JSF将抛出验证错误,作为软安全功能。将这些列表的填充移动到生命周期钩子(一个公共的void no-args方法,注释为@PostConstruct),它将在bean被上下文实例化之后立即运行

我假设您为了ajax(糟糕的设计)而使用会话范围的bean支持您的视图。将该bean更改为@ViewScoped以平滑您的设计并享受您的ajax处理