As you can see in the screenshot, I am having a JSF implicit value validation error for the property "member.city". The purpose is to -through ajax - populate the city select One Menu after selecting a country on the previous Country Select one menu. This is achieved by using selectOneMenu change listener and ajax to update the component displaying list of cities. However, I get a validation error and the form is not submitted.
正如您在屏幕截图中看到的,我对属性“member.city”有一个JSF隐式值验证错误。目的是-通过ajax -填充城市选择一个菜单后,在上一个国家选择一个菜单。这是通过使用selectOneMenu更改侦听器和ajax实现的,以更新城市的组件显示列表。但是,我得到一个验证错误,并且表单没有提交。
I tried with Entities with and without (hascode and equals methods), but in vain. I used a separated Managed Bean ViewScoped to retrive the filtered list, but in vain. **
我尝试了与没有(hascode和equals方法)的实体,但是没有用。我使用了一个分离的托管Bean ViewScoped来检索已过滤的列表,但没有用。* *
If I replace the filtered list of cities by the list that retrieves all cities (in the xhtml form, replace
<f:selectItems value="#{cityBean.citiesByCountry}" />
by<f:selectItems value="#{cityBean.all}" />
, there's no validation error and the form successfully gets submitted.如果我用检索所有城市的列表替换过滤后的城市列表(在xhtml表单中,替换
,没有验证错误,表单成功提交。
** Here is the code:
**这里是代码:
Form:
形式:
<p:outputLabel for="country" value="#{i18n.country}" />
<p:selectOneMenu valueChangeListener="#{cityBean.selectCountry}" rendered="true" id="country" value="#{newMember.country}" converter="#{countryBean.converter}"
style="width:160px;text-align:left;border:thin solid gray;"
required="true" requiredMessage="#{i18n.required}">
<f:selectItems value="#{countryBean.all}" var="countryname" itemLabel="#{countryname.name}"/>
<p:ajax event="change" update="city" />
</p:selectOneMenu>
<p:message for="country" />
<p:outputLabel for="city" value="#{i18n.city}" />
<p:selectOneMenu rendered="true" id="city" value="#{newMember.city}" converter="#{cityBean.converter}" converterMessage="Conversion error!"
style="width:160px;text-align:left;border:thin solid gray;"
required="true" requiredMessage="#{i18n.required}">
<f:selectItems value="#{cityBean.citiesByCountry}" var="cityname" itemLabel="#{cityname.cityname}"/>
</p:selectOneMenu>
<p:message for="city" />
City Entity head:
城市实体头:
@XmlRootElement
@Entity
@Table(name = "city", schema = "public")
@NamedQueries({
@NamedQuery(name = "City.findAll", query = "SELECT c FROM City c"),
@NamedQuery(name = "City.findById", query = "SELECT c FROM City c WHERE c.id = :id"),
@NamedQuery(name = "City.findByCountry", query = "SELECT c FROM City c WHERE c.country = :country"),
@NamedQuery(name = "City.findByCountryOrderedByName", query = "SELECT c FROM City c WHERE c.country = :country ORDER BY c.cityname ASC") })
@NamedNativeQueries({ @NamedNativeQuery(name = "City.findCountryNativeSQL", query = "select * from city c where c.countrybeta = :country.betacode", resultClass = City.class) })
public class City implements java.io.Serializable {
private static final long serialVersionUID = 3364735938266980295L;
private int id;
private Integer version;
private Country country;
private String cityname;
private String district;
private int population;
private Set<Member> members = new HashSet<Member>(0);
public City() {
}
// ...remainder of entity code (contructors and fields..)
The Bean:
豆:
@Named
@Stateful
@ConversationScoped
public class CityBean implements Serializable {
private static final long serialVersionUID = 1L;
private City city;
private List<City> cityList;
public City getCity() {
return this.city;
}
private Country selectedCountry;
@Inject
private Conversation conversation;
@PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;
// remainder of code....
// The List that works fine with Select one Menu
public List<City> getAll() {
CriteriaQuery<City> criteria = this.entityManager.getCriteriaBuilder()
.createQuery(City.class);
return this.entityManager.createQuery(
criteria.select(criteria.from(City.class))).getResultList();
}
@PostConstruct
public void init() {
setCityList(getCitiesByCountry());
}
public List<City> getCityList() {
return cityList;
}
public void setCityList(List<City> cityList) {
this.cityList = cityList;
}
/**
* The List That triggers a jsf value validation error
* @return List of Cities by Country ordered by city name
*/
public List<City> getCitiesByCountry() {
TypedQuery<City> query = this.entityManager.createNamedQuery(
"City.findByCountryOrderedByName", City.class);
query.setParameter("country", selectedCountry);
return query.getResultList();
}
// The Select One Menu Listener
public void selectCountry(ValueChangeEvent event) {
if (event != null) {
selectedCountry = (Country) event.getNewValue();
}
}
// getters and setters....
ENV: - JSF 21 - Hibernate - JBoss AS 7.1 - PrimeFaces 3.5 - Linux 3.5 / Firefox 18.0.2
ENV: - JSF 21 - Hibernate - JBoss AS 7.1 - PrimeFaces 3.5 - Linux 3.5 / Firefox 18.0.2
1 个解决方案
#1
3
Your concrete problem is caused because you aren't starting the conversation in @PostConstruct
and thus the bean behaves like request scoped. This way the list of cities will incompatibly change during processing the form submit and become empty (and thus the selected item couldn't be found in the list of available items and hence this validation error).
造成具体问题的原因是您没有在@PostConstruct中启动对话,因此bean的行为类似于请求作用域。这样,在处理表单提交时,城市列表将不兼容地更改并变为空(因此,在可用项目列表中无法找到所选的项目,因此出现此验证错误)。
You need to start the conversation in the @PostConstruct
.
您需要在@PostConstruct中启动对话。
conversation.begin();
See also:
- How to replace @ManagedBean / @ViewScope by CDI in JSF 2.0/2.1
- 如何用CDI替换JSF 2.0/2.1中的@ManagedBean / @ViewScope
- Validation Error: Value is not valid
- 验证错误:值无效
Unrelated to the concrete problem, using valueChangeListener
and performing business logic in getter methods is the wrong approach. You should not be performing business logic in getter methods at all. This is plain inefficient. They should solely return already-prepared bean properties.
与具体问题无关,使用valueChangeListener并在getter方法中执行业务逻辑是错误的方法。您不应该在getter方法中执行业务逻辑。这是低效的。它们应该只返回已经准备好的bean属性。
Rewrite your logic as follows:
重写你的逻辑如下:
<p:selectOneMenu value="#{bean.country}" ...>
<f:selectItems value="#{bean.countries}" />
<p:ajax listener="#{bean.updateCities}" update="city" />
</p:selectOneMenu>
<p:selectOneMenu id="city" value="#{bean.city}" ...>
<f:selectItems value="#{bean.cities}" />
</p:selectOneMenu>
with
与
private Country country;
private List<Country> countries;
private City city;
private List<City> cities;
@PostConstruct
public void init() {
countries = service.listCountries();
public void updateCities() {
cities = service.listCities(country);
}
// Add/generate normal!! getters/setters.
See also:
- Why JSF calls getters multiple times
- 为什么JSF会多次调用getters
- Our
selectOneMenu
tag wiki page - 我们的selectOneMenu标签维基页面
#1
3
Your concrete problem is caused because you aren't starting the conversation in @PostConstruct
and thus the bean behaves like request scoped. This way the list of cities will incompatibly change during processing the form submit and become empty (and thus the selected item couldn't be found in the list of available items and hence this validation error).
造成具体问题的原因是您没有在@PostConstruct中启动对话,因此bean的行为类似于请求作用域。这样,在处理表单提交时,城市列表将不兼容地更改并变为空(因此,在可用项目列表中无法找到所选的项目,因此出现此验证错误)。
You need to start the conversation in the @PostConstruct
.
您需要在@PostConstruct中启动对话。
conversation.begin();
See also:
- How to replace @ManagedBean / @ViewScope by CDI in JSF 2.0/2.1
- 如何用CDI替换JSF 2.0/2.1中的@ManagedBean / @ViewScope
- Validation Error: Value is not valid
- 验证错误:值无效
Unrelated to the concrete problem, using valueChangeListener
and performing business logic in getter methods is the wrong approach. You should not be performing business logic in getter methods at all. This is plain inefficient. They should solely return already-prepared bean properties.
与具体问题无关,使用valueChangeListener并在getter方法中执行业务逻辑是错误的方法。您不应该在getter方法中执行业务逻辑。这是低效的。它们应该只返回已经准备好的bean属性。
Rewrite your logic as follows:
重写你的逻辑如下:
<p:selectOneMenu value="#{bean.country}" ...>
<f:selectItems value="#{bean.countries}" />
<p:ajax listener="#{bean.updateCities}" update="city" />
</p:selectOneMenu>
<p:selectOneMenu id="city" value="#{bean.city}" ...>
<f:selectItems value="#{bean.cities}" />
</p:selectOneMenu>
with
与
private Country country;
private List<Country> countries;
private City city;
private List<City> cities;
@PostConstruct
public void init() {
countries = service.listCountries();
public void updateCities() {
cities = service.listCities(country);
}
// Add/generate normal!! getters/setters.
See also:
- Why JSF calls getters multiple times
- 为什么JSF会多次调用getters
- Our
selectOneMenu
tag wiki page - 我们的selectOneMenu标签维基页面