1

我有

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>

代码做了应该做的事情,但是我在第一行得到了上面提到的错误,只有当我点击英格兰并选择国家选项时,我不知道为什么,我在 Ajaxized 代码中编写了相同的任务,它工作得很好, 任何 手 都会 心存感激 .

4

3 回答 3

3

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

  1. equals()项目值类型的方法丢失或损坏。
  2. 提交期间可用项目列表发生了不兼容的更改。

item 的值类型是String,所以它的equals()方法无疑是正确实现的(否则它会破坏 Java 世界中的一切)。所以原因1可能会被划伤。剩下的原因 2。是的,确实,由于某些不清楚的原因,您正在清空国家/地区的可用城市列表。

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

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

这一切都只是破坏的代码设计。Getter 根本不应该包含任何业务逻辑。Getter 应该只返回 bean 属性。这些 bean 属性应该早就在(post)构造函数或 action(listener)方法中预先初始化。

如何解决这个问题是一个很好的第二个问题。这里<h:selectOneMenu valueChangeListener>基本上被滥用了。这是工作的错误工具。您应该<f:ajax listener>改为使用它。但是,如果您真的坚持滥用valueChangeListenerfor 作业,那么您应该按如下方式解决它,即您将ValueChangeEventtoINVOKE_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>

也可以看看:

于 2012-12-04T18:44:27.180 回答
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

于 2012-12-04T15:38:08.540 回答
1

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

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

于 2012-12-04T16:50:44.843 回答