根据 p:dataTable 的每一行中的另一个 p:selectOneMenu 填充 p:selectOneMenu [英] Populate p:selectOneMenu based on another p:selectOneMenu in each row of a p:dataTable

查看:13
本文介绍了根据 p:dataTable 的每一行中的另一个 p:selectOneMenu 填充 p:selectOneMenu的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有延迟加载的 .在其中的两列中,每列中都有一个 .

I have a <p:dataTable> with lazy load. In two of the columns, there is a <p:selectOneMenu> in each of them.

第一列包含国家/地区列表,第二列包含来自数据库的国家/地区列表.

The first column holds a list of countries and the second one holds a list of states from a database.

我希望第二个菜单(包含州列表的菜单)仅显示数据表每一行中与中第一个菜单中的国家/地区相对应的那些州数据表的每一行.

I want the second menu (the one that contains a list of states) to show only those states in each row of the data table which correspond to the country in the first menu in each row of the data table.

在编辑模式下,当菜单中的某个国家/地区发生更改时,应在当前行的菜单中填充与该国家/地区对应的国家/地区.

During edit mode, when a country in its menu is changed, the states corresponding to that country should be populated in its menu in that current row.

如何在数据表的每一行中加载与其国家相对应的国家列表?

How to load such lists of states that correspond to their countries in each row of the data table?

数据表中的这两列不完整,因为我不知道如何实现这一点.

These two columns in the data table are left incomplete, since I don't have a precise idea about how to achieve this.

<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>

cityBean.selectedCountries 检索所有必要的国家,但 cityBean.selectedStates 也从数据库中检索所有不必要的国家,应该修改为仅检索那些在另一个菜单中对应其国家/地区的状态.

cityBean.selectedCountries retrieves all the countries which is necessary but cityBean.selectedStates also retrieves all the states from the database which is unnecessary and should be modified to retrieve only those states which correspond to its country in another menu.

我如何从这里开始?

推荐答案

虽然您的初始解决方案有效,但实际上效率很低.这种方法基本上要求 CountryState 表的整个对象图(即使有循环引用)在每个 JSF 视图或会话的 Java 内存中完全加载,即使当你同时仅使用例如150 个国家中的 5 个(因此理论上 5 个州列表就足够了,而不是 150 个州列表).

Whilst your initial solution works, it's in fact inefficient. This approach basically requires the entire object graph of the Country and State tables (even with circular references) to be fully loaded in Java's memory per JSF view or session even though when you simultaneously use only e.g. 5 of 150 countries (and thus theoretically 5 state lists would have been sufficient instead of 150 state lists).

我没有完全了解您的功能和技术要求.也许您实际上同时使用了所有这 150 个国家/地区.也许您有很多页面需要所有(至少,许多")国家和州.也许您拥有最先进的服务器硬件和充足的内存,以便可以在内存中的所有 JSF 视图和 HTTP 会话中轻松复制所有国家和州.

I don't have full insight into your functional and technical requirements. Perhaps you're actually simultaneously using all of those 150 countries. Perhaps you've many pages where all (at least, "many") countries and states are needed. Perhaps you've state of art server hardware with plenty of memory so that all countries and states can effortlessly be duplicated over all JSF views and HTTP sessions in memory.

如果情况并非如此,那么不要急切地获取每个国家的州列表将是有益的(即 @OneToMany(fetch=LAZY) 应该是用于 Country#statesState#cities).鉴于国家和州列表是(可能)静态数据,在一年内变化很少,至少足以仅在每次部署的基础上进行更改,最好将它们存储在应用程序范围的 bean 中,该 bean 可以跨所有视图和会话,而不是在每个 JSF 视图或 HTTP 会话中复制.

If that's not the case, then it would be beneficial to not eagerly fetch the state list of every single country (i.e. @OneToMany(fetch=LAZY) should be used on Country#states and State#cities). Given that country and state lists are (likely) static data which changes very few times in a year, at least sufficient to be changed on a per-deploy basis only, it's better to just store them in an application scoped bean which is reused across all views and sessions instead of being duplicated in every JSF view or HTTP session.

在继续回答之前,我想指出您的代码中存在逻辑错误.鉴于您正在编辑城市列表,因此 #{row} 本质上是 #{city},您通过 state 引用国家很奇怪与下拉输入值中的 #{city.state.country} 一样.虽然这可能适用于显示,但不适用于编辑/保存.基本上,您在这里是在每个州的基础上而不是在每个城市的基础上更改国家/地区.当前选定的州将获得新的国家而不是当前迭代的城市.这种变化将反映在该州的所有城市中!

Before continuing to the answer, I'd like to remark that there's a logic error in your code. Given the fact that you're editing a list of cities, and thus #{row} is essentially #{city}, it's strange that you reference the country via the state as in #{city.state.country} in the dropdown input value. Whilst that may work for displaying, that wouldn't work for editing/saving. Basically, you're here changing the country on a per-state basis instead of on a per-city basis. The currently selected state would get the new country instead of the currently iterated city. This change would get reflected in all cities of this state!

如果您想继续使用此数据模型,这确实不是一件小事.理想情况下,您希望在 City 上拥有一个单独的(虚拟)Country 属性,以便更改不会影响城市的 State 属性.您可以将其设置为 @Transient,这样 JPA 就不会默认将其视为 @Column.

This is indeed not trivial if you'd like to continue with this data model. Ideally, you'd like to have a separate (virtual) Country property on City so that the changes doesn't affect the city's State property. You could make it just @Transient so that JPA doesn't consider it as a @Column as by default.

@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;
    }
}

总而言之,您最终应该拥有这个(为简洁起见省略了不相关/默认/明显的属性):

All in all, you should ultimately have this (irrelevant/default/obvious attributes omitted for brevity):

<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>

使用 #{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;
    }

}

(这是延迟加载方法;您也可以立即在 @PostConstruct 中获取它们,看看哪个更适合您)

(this is the lazy loading approach; you could also immediately fetch them all in @PostConstruct, just see what's better for you)

这篇关于根据 p:dataTable 的每一行中的另一个 p:selectOneMenu 填充 p:selectOneMenu的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆