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

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

问题描述

我有一个延迟加载的<p:dataTable>.在两列中,每列中都有一个<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个国家/地区.也许您有很多页面需要 all (至少许多")国家和地区.也许您拥有具有足够内存的先进服务器硬件,因此可以轻松地在内存中的所有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中,以便在整个范围内重复使用所有视图和会话,而不是在每个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},奇怪的是您通过下拉输入值中的#{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天全站免登陆