填充p:selectOneMenu基于另一个p:selectOneMenu在ap:dataTable的每一行

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

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

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

如果情况并非如此,那么急于获取每个国家的州名单(即@OneToMany(fetch=LAZY)应该使用Country#statesState#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,只看看对你有什么好处)

  • 别客气.赏金没问题.问题在"特色"列表中保留的时间越长,最终您对某些upvotes的机会就越多,这样您就可以获得所花费的声誉. (3认同)