Tin*_*iny 12 jsf selectonemenu primefaces jsf-2.2
我有一个<p:dataTable>懒惰的负载.在其中两列中,<p:selectOneMenu>每列都有一个.
第一列包含国家/地区列表,第二列包含数据库中的状态列表.
我想第二个菜单显示在只有那些状态(包含州列表的)每一行对应于该国在第一菜单中的数据表的每一行数据表.
在编辑模式下,当其菜单中的国家/地区更改时,应在其当前行的菜单中填充与该国家/地区对应的状态.
如何在数据表的每一行中加载与其国家/地区对应的状态列表?
数据表中的这两列是不完整的,因为我对如何实现这一点没有一个确切的想法.
<p:column>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{row.state.country.countryName}"/>
</f:facet>
<f:facet name="input">
<p:selectOneMenu value="#{row.state.country}">
<f:selectItems var="country"
value="#{cityBean.selectedCountries}"
itemLabel="#{country.countryName}"
itemValue="#{country}"/>
<p:ajax update="states" listener="#{cityBean.getStates}"/>
</p:selectOneMenu>
</f:facet>
</p:cellEditor>
</p:column>
<p:column>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{row.state.stateName}"/>
</f:facet>
<f:facet name="input">
<p:selectOneMenu id="states">
<f:selectItems var="state"
value="#{cityBean.selectedStates}"
itemLabel="#{state.stateName}"
itemValue="#{state}"/>
</p:selectOneMenu>
</f:facet>
</p:cellEditor>
</p:column>
Run Code Online (Sandbox Code Playgroud)
cityBean.selectedCountries检索所有必需的国家,但cityBean.selectedStates也从数据库中检索所有不必要的状态,并且应该修改这些状态以仅检索与另一个菜单中的国家相对应的那些状态.
我怎么能从这里开始?
Bal*_*usC 13
虽然您的初始解决方案有效,但实际上效率低下.这种方法基本上要求Country和State表的整个对象图(甚至是循环引用)在每个JSF视图或会话的Java内存中完全加载,即使你同时只使用150个国家中的5个(因此理论上5个州列表会已经足够而不是150个州名单).
我没有完全了解您的功能和技术要求.也许你实际上同时使用了所有这150个国家.也许你有很多页面需要所有(至少"很多")国家和州.也许您拥有大量内存的最先进的服务器硬件,因此所有国家/地区都可以毫不费力地在内存中的所有JSF视图和HTTP会话上进行复制.
如果情况并非如此,那么不急于获取每个国家的州名单(即@OneToMany(fetch=LAZY)应该使用Country#states和State#cities)将是有益的.鉴于国家和州列表(可能)静态数据在一年内变化很少,至少足以仅在每个部署的基础上进行更改,最好将它们存储在应用程序范围内的bean中,该bean可以重复使用所有视图和会话,而不是在每个JSF视图或HTTP会话中重复.
在继续回答之前,我想说一下你的代码中存在逻辑错误.鉴于您正在编辑城市列表,因此#{row}实际上#{city},您通过状态#{city.state.country}在下拉输入值中引用国家,这很奇怪.虽然这可能适用于显示,但这不适用于编辑/保存.基本上,你在这里按州而不是按城市改变国家.当前选定的州将获得新的国家而不是当前迭代的城市.这种变化将反映在这个州的所有城市!
如果您想继续使用此数据模型,这确实不是一件容易的事.理想情况下,您希望拥有一个单独的(虚拟)Country属性,City以便更改不会影响城市的State属性.您可以这样做,@Transient以便JPA @Column默认情况下不会将其视为默认值.
@Transient // This is already saved via City#state#country.
private Country country;
public Country getCountry() {
return (country == null && state != null) ? state.getCountry() : country;
}
public void setCountry(Country country) {
this.country = country;
if (country == null) {
state = null;
}
}
Run Code Online (Sandbox Code Playgroud)
总而言之,你应该最终拥有这个(为了简洁省略了不相关/默认/明显的属性):
<p:dataTable value="#{someViewScopedBean.cities}" var="city">
...
<p:selectOneMenu id="country" value="#{city.country}">
<f:selectItems value="#{applicationBean.countries}" />
<p:ajax update="state" />
</p:selectOneMenu>
...
<p:selectOneMenu id="state" value="#{city.state}">
<f:selectItems value="#{applicationBean.getStates(city.country)}" />
</p:selectOneMenu>
...
</p:dataTable>
Run Code Online (Sandbox Code Playgroud)
有这样的#{applicationBean}事情:
@Named
@ApplicationScoped
public class ApplicationBean {
private List<Country> countries;
private Map<Country, List<State>> statesByCountry;
@EJB
private CountryService countryService;
@EJB
private StateService stateService;
@PostConstruct
public void init() {
countries = countryService.list();
statesByCountry = new HashMap<>();
}
public List<Country> getCountries() {
return countries;
}
public List<State> getStates(Country country) {
List<State> states = statesByCountry.get(country);
if (states == null) {
states = stateService.getByCountry(country);
statesByCountry.put(country, states);
}
return states;
}
}
Run Code Online (Sandbox Code Playgroud)
(这是延迟加载的方法;你也可以立即将它们全部取出@PostConstruct,只看看对你有什么好处)
| 归档时间: |
|
| 查看次数: |
7974 次 |
| 最近记录: |