JSF 2.0:リクエストスコープBeanを使用して初期データを含むフォームフィールドをレンダリングします(現在のコンポーネントに固有のサーバーデータを更新するため)
-
26-09-2019 - |
質問
ごく普通のケース:
次の 2 つの状態を持つ「ポートレット」複合コンポーネントがあります。広がったり潰れたり。ポートレットは展開された状態で開始されますが、ユーザーはポートレットをクリックして折りたたむことができます。この状態はセッションに保存する必要があります。これにより、ユーザーがページを更新するか、新しいページに移動すると、ポートレットが展開されているか折りたたまれているかが記憶されます。
これが問題の核心です:ページに何度も挿入できるこのような状態保存をコンポーネントに実際に実装するにはどうすればよいでしょうか?
いくつかの背景/アイデア:
1 つのポートレットの状態保存の実装は、セッション スコープの Bean を使用することで簡単に実現できます。また、固定数のセッション スコープの Bean を作成するか、異なるプロパティを 1 つの Bean に宣言することで、固定数のポートレットをサポートできます。ただし、これらのアプローチがなぜ悪いのかについては言及したくありません。
これに対する私の最初のアイデアは、単一のセッション Bean の下にマップ (または何らかのオブジェクト構造) を作成して、すべてのポートレットの状態を保持することでした。ただし、このデータを JSF から直接参照することはできません (JSF も更新するため)。マップ内の正しい値をフェッチ/更新するために特定のゲッター/セッターを作成することを検討しましたが、ゲッターとセッターの実行中に識別データがないため、それは不可能です。
ただし、状態をセッション Bean に保存する必要があることはおそらく明らかです。フォームを投稿することで、非常に簡単に状態保存を行うことができます。 f:ajax
を使用して特別なメソッドを実行します listener
そして提出されたデータ。コンポーネントの複数のインスタンスをサポートするには、リクエスト スコープの Bean (の複数のインスタンス) を使用して、各展開/折りたたみを処理できます。ただし、ポートレットとその状態を識別するデータを実際に投稿するには、まずレンダリング時にフォーム フィールドにデータを挿入する必要があります。
では、レンダリング時に各ポートレットに実際に適切なデータを提供するにはどうすればよいでしょうか (実際には、この場合は単に boolean/enum を指定するだけですが、より多くのデータを処理する必要がある場合を考慮してください)。
のようだ:
h:inputHidden
そしてh:inputText
value 属性以外の初期値の設定はサポートされていません (これは、#{portletBean.portletState}
).- 識別情報が利用できないため、リクエスト Bean を作成時に正しい初期値で自動ロードできません。
またしても何かが足りないような気がします...ポインタ?
少なくとも 1 つの答えは次のとおりだと思います。 UIComponent
複合コンポーネントではなく、生の HTML 要素が多数含まれているため、これを複合コンポーネントとして維持したいと思います。
アップデート:
- があります かなり似たような質問, 、ビュースコープの Bean を使用することが提案されています。ただし、これはここでは実現できないと思います。
解決 3
私が使用したのは、 JS から Bean メソッドを呼び出すための以前に解決されたハック:
空の非表示のフォームを作成しました。 h:commandButton
そして f:ajax
その中で呼んでいる listener
.
展開/折りたたみイベントを起動するための別のボタンを作成しました。そのボタンから JavaScript メソッドを開始し、展開/折りたたみをアニメーション化し、同時にフォーム内の隠しボタンをクリックします。必要なデータはすべて、 listener
属性。リスナーは、コンポーネントのクライアント ID によって識別される状態のマップをチェック/更新するセッション Bean をターゲットとします。
これは非常に単純なことですが、なぜもっと早く気づかなかったのか不思議です...
ただし、まだ完璧には程遠く、リクエストスコープの Bean をデフォルト値で開始してストレージ/トランスポート形式として使用する方法については答えていません。
他のヒント
私は、このためのいくつかの可能なハックを得ました。私はここに掲載しますが、私はこれらが本当に進むべき道とは思わない:
を使用して直接レンダリング後のセットのフォームフィールドの右データとのにはJavaScriptます:
window.addEvent('domready', function() {
var portletState = '#{portletBean.getPortletState(cc.attrs.clientId)}';
// find the field and set the data...
});
veeryハック感じています。私はこのルートを取る必要はありません。
のデータを保持するために使用するコンポーネントの属性マップ:の
<cc:interface>
<cc:attribute name="portletState" default="#{portletBean.getPortletState(cc.attrs.clientId)}" />
</cc:interface>
アクセスコードからそれます:
public void stateChangedCallback(String clientId) {
UIComponent component = FacesContext.getCurrentInstance().getViewRoot().findComponent(clientId);
String state = (String) component.getAttributes().get("portletState");
}
また、右感じることはありません。また、私はそれは(あなたが実際にデフォルトの属性でELを使用することができます)動作するかどうかを確認していない。
コンポーネントを呼び出すときに、それを提供することで、データを保持するために、を使用し、コンポーネントの属性マップの
<portlet:portlet id="specificPortlet">
<f:attribute name="portletState" value="#{portletBean.getPortletState(component.clientId)}" />
</portlet:portlet>
ここでも、非常に不器用なとあなたのコードを複製します。
あなたは、この使用してタグライブラリを行うことができ、あなたがタグにパラメータを渡すことができます:
あなたのタグライブラリに以下を追加します:
<tag>
<tag-name>date</tag-name>
<source>components/date.xhtml</source>
</tag>
そして、ここでのコンポーネントの内容がある/ date.xhtml
<ui:composition name="date_template">
<h:panelGrid id="#{id}Panel" columns="1" border="0" cellpadding="0" cellspacing="0">
<rich:calendar immediate="true" id="#{id}" popup="true" datePattern="dd.MM.yyyy"
enableManualInput="true" showApplyButton="true" cellWidth="12px" cellHeight="11px" style="width:100px"
value="#{dateForm.date}" required="#{required}" readonly="#{readonly}" validator="#{dateForm.validateDate}">
<a4j:support event="onchanged" reRender="#{id}Panel,#{reRenderDate}"/>
<ui:insert />
</rich:calendar>
<rich:message for="#{id}" errorClass="error" />
</h:panelGrid>
</ui:composition>
そして、あなたのようにこれを使用します:
<adse:date id="dateTransfert" required="true"
dateForm="#{dossierBean.dateTransfert}"
readonly="false" reRenderDate="montantAncien,montantNouveau,montantEmolument">
<a4j:support event="oninputblur" reRender="dateTransfertPanel,montantAncien,montantNouveau,montantEmolument"/>
</adse:date>
この場合、DATEFORMが渡されたは、これはオブジェクトであり、タグライブラリでは、そのオブジェクト(dateForm.date)のプロパティを使用します。
また注意:XHTML内の他の要素を含めるために使用することができる
だから、あなたは、各ポートレットのコンテキストに渡すことができます。あなたもgetPortletState()メソッドを定義してスーパークラスとしてportletBeanを持っていることによって、この規格を作ることができます。