13

私は<p:dataTable>怠惰な負荷を持っています。2 つの列には、<p:selectOneMenu>それぞれに があります。

最初の列には国のリストが保持され、2 番目の列にはデータベースからの州のリストが保持されます。

2 番目のメニュー (州のリストを含むもの) に、データ テーブルの各行の最初のメニューの国に対応するデータ テーブルの各行の州のみを表示する必要があります。

編集モード中にメニューの国が変更されると、その国に対応する州が現在の行のメニューに表示されます。

データ テーブルの各行にある国に対応する州のリストを読み込む方法は?


データ テーブルのこれら 2 つの列は、これを実現する方法について正確なアイデアがないため、不完全なままです。

<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取得しますが、不要なすべての州もデータベースから取得し、別のメニューでその国に対応する州のみを取得するように変更する必要があります。

ここからどうすれば進めますか?

4

3 に答える 3

13

最初のソリューションは機能しますが、実際には非効率的です。このアプローチでは、たとえば 150 か国中 5 か国のみを同時に使用する場合でも (したがって、理論的CountryStateは 5 つの州リストは150 の状態リストの代わりに十分でした)。

私はあなたの機能的および技術的要件について十分な洞察を持っていません. おそらく、実際には 150 か国すべてを同時に使用しているのでしょう。おそらく、すべての(少なくとも「多くの」) 国と州が必要なページがたくさんあるでしょう。おそらく、メモリ内のすべての JSF ビューと HTTP セッションですべての国と州を簡単に複製できるように、十分なメモリを備えた最先端のサーバー ハードウェアを使用しているでしょう。

そうでない場合は、すべての国の州リストを熱心にフェッチしない方が有益です (つまり、および@OneToMany(fetch=LAZY)で使用する必要があります)。国と州のリストは (おそらく) 年にほとんど変更されない静的データであり、少なくともデプロイごとに変更するだけで十分であることを考えると、それらをアプリケーション スコープの Bean に保存し、全体で再利用することをお勧めします。すべての JSF ビューまたは HTTP セッションで複製されるのではなく、すべてのビューとセッション。Country#statesState#cities

回答に進む前に、コードに論理エラーがあることに注意してください。都市のリストを編集しているという事実を考えると、#{row}本質的には、ドロップダウンの入力値の#{city}ように州を介して国を参照するのは奇妙です。#{city.state.country}表示には機能するかもしれませんが、編集/保存には機能しません。基本的に、ここでは、都市ごとではなく、州ごとに国を変更しています。現在選択されている州は、現在繰り返されている都市ではなく、新しい国を取得します。この変更は、この州のすべての都市に反映されます。

このデータ モデルを継続したい場合、これは確かに簡単なことではありません。理想的には、別の (仮想)CountryプロパティをオンにCityして、変更が都市のStateプロパティに影響を与えないようにすることをお勧めします。@TransientJPAがデフォルトで as と見なさないようにすることができ@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;
    }
}

全体として、最終的にはこれが必要です(簡潔にするために、無関係/デフォルト/明らかな属性は省略されています):

<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何が良いかを確認してください)

于 2014-05-08T07:49:49.460 に答える
0

選択した国に基づいて州リストを取得する必要があります。そのためには valueChangeListener が必要です。

これを試してください(今のところ、選択したメニューの両方を同じ列に入れました)

あなたのxhtmlで

<p:column id="state" headerText="State" sortBy="#{row.state.stateName}" filterBy="#{row.state.stateName}">
<p:cellEditor>  
        <f:facet name="output">  
        <f:facet name="output">
                    <h:outputLink value="State.jsf">
                        <h:outputText value="#{row.state.stateName}"/>
                        <f:param name="id" value="#{row.state.stateId}"/>
                    </h:outputLink>
            </f:facet>
        <f:facet name="input">  
        <h:selectOneMenu id="cmbCountryMenu" style="width:100px" value="#{row.state.country}" converterMessage="Error message." label="Country" valueChangeListener = "#{countryController.handleCountrySelect}" immediate="true"  converter="#{countryConverter}">  
            <f:selectItem itemLabel = "Select" itemValue="#{null}" /> 
            <f:selectItems var="country" value="#{cityManagedBean.selectedCountries}" itemLabel="#{country.countryName}" itemValue="#{country}"/>
            <p:ajax update="cmbStateMenu" />
        </h:selectOneMenu>
        <h:selectOneMenu style="width:100px" value="#{row.state}" valueChangeListener = "#{stateController.handleStateSelect}" immediate="false" id="cmbStateMenu"  converter = "#{stateConverter}">

             <f:selectItems var="state" value="#{row.state.country.stateTableSet}" itemLabel="#{state.stateName}" itemValue="#{state}" itemLabelEscaped="true" rendered="true"/>
            <p:ajax update="@this" />
        </h:selectOneMenu>
        </f:facet>  
    </p:cellEditor> 

あなたのコントローラーで

  public void handleCountrySelect( ValueChangeEvent event )
{
    setStates( ( ( Country) event.getNewValue() ) );
}
于 2014-05-07T20:15:53.500 に答える
0

この場合、それは非常に簡単です。これ以上コーディングする必要はありません。状態メニューでは、以下、

<f:selectItems var="state" value="#{cityManagedBean.selectedStates}" 
               itemLabel="#{state.stateName}" itemValue="#{state}" 
               itemLabelEscaped="true" rendered="true"/>

以下のように変更するだけです。

<f:selectItems var="state" value="#{row.state.country.stateTableSet}" 
               itemLabel="#{state.stateName}" itemValue="#{state}" 
               itemLabelEscaped="true" rendered="true"/>

エンティティ オブジェクト (rowこの場合) には埋め込みオブジェクトがstate含まれており、そのオブジェクトには、それに対応するcountry状態のリストが最終的に含まれている ことが明らかです。country

となることによって

cityManagedBean.selectedStates

追加のマネージド Bean メソッドはまったく必要なくなり、次のように変更する必要がありました。

row.state.country.stateTableSet

stateTableSetは、エンティティSet<StateTable>のオブジェクトのリストを含むStateTableです。


また、listenerin<p:ajax>は不要になりました。次のようになります。

<p:ajax update="cmbStateMenu"/>

国メニューで項目 (国) が選択されたときに、状態メニューを更新する目的でのみ存在します。


問題のコードは次のようになります。

<p:column id="country" headerText="Country" resizable="true" sortBy="#{row.state.country.countryName}" filterBy="#{row.state.country.countryName}" filterMatchMode="contains" filterMaxLength="45">
    <p:cellEditor>
        <f:facet name="output">
            <h:outputLink value="Country.jsf">
                <h:outputText value="#{row.state.country.countryName}"/>
                <f:param name="id" value="#{row.state.country.countryId}"/>
            </h:outputLink>                                                                                
        </f:facet>
        <f:facet name="input">
            <p:selectOneMenu id="cmbCountryMenu" converter="#{countryConverter}" value="#{row.state.country}" label="Country" required="true" filter="true" filterMatchMode="contains" effect="fold" rendered="true" editable="false" style="width:100%;">
                <f:selectItems var="country" value="#{cityManagedBean.selectedCountries}" itemLabel="#{country.countryName}" itemValue="#{country}" itemLabelEscaped="true" rendered="true"/>
                <p:ajax update="cmbStateMenu"/>
            </p:selectOneMenu>
        </f:facet>
    </p:cellEditor>
</p:column>

<p:column id="state" headerText="State" resizable="false" sortBy="#{row.state.stateName}" filterBy="#{row.state.stateName}" filterMatchMode="contains" filterMaxLength="45">
    <p:cellEditor>
        <f:facet name="output">
            <h:outputLink value="State.jsf">
                <h:outputText value="#{row.state.stateName}"/>
                <f:param name="id" value="#{row.state.stateId}"/>
            </h:outputLink>
        </f:facet>
        <f:facet name="input">
            <p:selectOneMenu id="cmbStateMenu" converter="#{stateConverter}" value="#{row.state}" label="State" required="true" filter="true" filterMatchMode="contains" effect="fold" rendered="true" editable="false" style="width:100%;">
                <f:selectItems var="state" value="#{row.state.country.stateTableSet}" itemLabel="#{state.stateName}" itemValue="#{state}" itemLabelEscaped="true" rendered="true"/>
            </p:selectOneMenu>
        </f:facet>
    </p:cellEditor>
</p:column>

お詫び : 質問の中で、都市のリストを取得しているとは言いませんでした。

于 2013-07-21T22:34:10.917 に答える