JSF2 FaceletsのJSTLは理にかなっていますか?


163

条件付きでFaceletsコードのビットを出力したいと思います。

そのため、JSTLタグは正常に機能するようです。

<c:if test="${lpc.verbose}">
    ...
</c:if>

ただし、これがベストプラクティスかどうかはわかりません。私の目標を達成する別の方法はありますか?

回答:


320

前書き

JSTL <c:xxx>タグはすべてタグハンドラであり、ビューのビルド時に実行され<h:xxx>ますが、JSF タグはすべてUIコンポーネントであり、ビューのレンダリング時に実行されます

JSF自身からという注意<f:xxx><ui:xxx>タグだけないものではないから延びるが、UIComponentまたtaghandlersあり、例えば<f:validator><ui:include><ui:define>、などから延びているものUIComponentもJSFのUIコンポーネントであり、例えば<f:param><ui:fragment><ui:repeat>JSF UIコンポーネントから、などのみidbinding属性でありますビューのビルド時にも評価されます。したがって、JSTLライフサイクルに関する以下の回答は、JSFコンポーネントのidおよびbinding属性にも適用されます。

ビューのビルド時間は、XHTML / JSPファイルが解析されてJSFコンポーネントツリーに変換されUIViewRoot、その時点で格納される瞬間FacesContextです。ビューのレンダリング時間は、JSFコンポーネントツリーがHTMLを生成しようとする瞬間UIViewRoot#encodeAll()です。そのため、コーディングから期待するとおり、JSF UIコンポーネントとJSTLタグは同期して実行されません。次のように視覚化できます。JSTLは最初に上から下に実行され、JSFコンポーネントツリーを生成します。次に、JSFが上から下に実行され、HTML出力を生成します。

<c:forEach><ui:repeat>

たとえば、このFaceletsマークアップは、次を使用して3つの項目を繰り返し処理します<c:forEach>

<c:forEach items="#{bean.items}" var="item">
    <h:outputText id="item_#{item.id}" value="#{item.value}" />
</c:forEach>

...ビューのビルド時<h:outputText>に、JSFコンポーネントツリーに3つの個別のコンポーネントを作成します。大まかに次のように表されます。

<h:outputText id="item_1" value="#{bean.items[0].value}" />
<h:outputText id="item_2" value="#{bean.items[1].value}" />
<h:outputText id="item_3" value="#{bean.items[2].value}" />

...ビューのレンダリング時にHTML出力を個別に生成します。

<span id="item_1">value1</span>
<span id="item_2">value2</span>
<span id="item_3">value3</span>

コンポーネントIDの一意性を手動で確認する必要があり、それらもビューのビルド時に評価されることに注意してください。

このFaceletsマークアップ<ui:repeat>は、JSF UIコンポーネントであるを使用して3つの項目を繰り返し処理しますが、

<ui:repeat id="items" value="#{bean.items}" var="item">
    <h:outputText id="item" value="#{item.value}" />
</ui:repeat>

...すでにJSFコンポーネントツリーでそのままの状態で終了します。これにより、非常に同じ<h:outputText>コンポーネントがビューのレンダリング時に再利用され、現在の反復ラウンドに基づいてHTML出力を生成します。

<span id="items:0:item">value1</span>
<span id="items:1:item">value2</span>
<span id="items:2:item">value3</span>

<ui:repeat>NamingContainerコンポーネントであるため、反復インデックスに基づいてクライアントIDの一意性がすでに保証されていることに注意してください。ELは、idビューの構築時にも評価され#{item}、ビューのレンダリング時にのみ使用できるため、このように子コンポーネントの属性でELを使用することもできません。同じことがh:dataTable類似のコンポーネントにも当てはまります。

<c:if>/ <c:choose>vsrendered

別の例として、このFaceletsマークアップは、条件付きで異なるタグを追加します<c:if><c:choose><c:when><c:otherwise>これにも使用できます):

<c:if test="#{field.type eq 'TEXT'}">
    <h:inputText ... />
</c:if>
<c:if test="#{field.type eq 'PASSWORD'}">
    <h:inputSecret ... />
</c:if>
<c:if test="#{field.type eq 'SELECTONE'}">
    <h:selectOneMenu ... />
</c:if>

... コンポーネントをJSFコンポーネントツリーにtype = TEXT追加するだけの場合<h:inputText>

<h:inputText ... />

このFaceletsマークアップでは:

<h:inputText ... rendered="#{field.type eq 'TEXT'}" />
<h:inputSecret ... rendered="#{field.type eq 'PASSWORD'}" />
<h:selectOneMenu ... rendered="#{field.type eq 'SELECTONE'}" />

...条件に関係なく、JSFコンポーネントツリーでは上記とまったく同じになります。したがって、これらのコンポーネントツリーが多数あり、それらが実際に「静的」モデルに基づいている場合(つまり、field少なくともビュースコープの間は変更されない)、これは「肥大化した」コンポーネントツリーになる可能性があります。また、2.2.7より前のバージョンのMojarraで追加のプロパティを持つサブクラスを処理すると、ELの問題が発生する可能性があります。

<c:set><ui:param>

互換性はありません。<c:set>セットのみアクセス可能であるELスコープ内の変数、した後、ビューのビルド時にタグの位置が、ビュー中のビュー内の任意の場所には、時間をレンダリングします。<ui:param>介し含まにfaceletテンプレートにEL変数を渡し<ui:include><ui:decorate template>または<ui:composition template>。古いJSFバージョンにはバグがあり、<ui:param>問題のFaceletテンプレートの外部でも変数を使用できるため、これに依存することはできません。

<c:set>なしのscope属性は、エイリアスのように動作します。EL式の結果はどのスコープにもキャッシュされません。したがって、JSFコンポーネントの繰り返しなどの内部で完全に使用できます。したがって、たとえば以下は正常に動作します。

<ui:repeat value="#{bean.products}" var="product">
    <c:set var="price" value="#{product.price}" />
    <h:outputText value="#{price}" />
</ui:repeat>

これは、ループで合計を計算する場合などには適していません。その代わりに使用します EL 3.0ストリームをます

<ui:repeat value="#{bean.products}" var="product">
    ...
</ui:repeat>
<p>Total price: #{bean.products.stream().map(product->product.price).sum()}</p>

のみ、あなたが設定した場合scope、許容値のいずれかを持つ属性をrequestviewsession、またはapplication、それはビューのビルド時に、すぐに評価され、指定されたスコープに保存されます。

<c:set var="dev" value="#{facesContext.application.projectStage eq 'Development'}" scope="application" />

これは一度だけ評価さ#{dev}れ、アプリケーション全体を通して利用できます。

JSTLを使用してJSFコンポーネントツリーの構築を制御する

JSTLを使用して予期しない結果が生じるの<h:dataTable><ui:repeat>、などのJSF反復コンポーネント内で使用した場合、またはJSTLタグ属性が次のようなJSFイベントの結果に依存している場合のみです。preRenderView、またはビューの構築時に利用できないモデルで送信されたフォーム値。そのため、JSTLコンポーネントツリー構築のフローを制御するためにのみJSTLタグを使用します。JSF UIコンポーネントを使用して、HTML出力生成のフローを制御します。var反復するJSFコンポーネントをJSTLタグ属性にバインドしないでください。JSTLタグ属性のJSFイベントに依存しないでください。

コンポーネントをを介してバッキングBeanにバインドするbindingか、を介してコンポーネントを取得findComponent()し、バッキングBeanでJavaコードを使用してその子を作成/操作する必要があると思うときはいつでもnew SomeComponent()する必要があると思われる場合は、すぐに停止し、代わりにJSTLの使用を検討してください。JSTLもXMLベースであるため、JSFコンポーネントを動的に作成するために必要なコードは、非常に読みやすく、保守しやすくなります。

知っておくべき重要なことは、2.1.18より前のバージョンのMojarraには、JSTLタグ属性でビュースコープBeanを参照するときに部分的な状態の保存に関するバグがあったことです。ビュースコープBean全体が、ビューツリーから取得されるのではなく、新しく再作成されます(JSTLの実行時点では、ビューツリー全体がまだ利用できないためです)。JSTLタグ属性によってビュースコープBeanの状態を予測または格納している場合、期待した値が返されないか、ビューの後で復元される実際のビュースコープBeanで「失われる」ツリーが構築されます。Mojarra 2.1.18以降にアップグレードできない場合の回避策はweb.xml、以下のように部分的な状態の保存をオフにすることです。

<context-param>
    <param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
    <param-value>false</param-value>
</context-param>

以下も参照してください。

JSTLタグが役立つ実際の例をいくつか見るには(つまり、ビューの構築中に実際に適切に使用した場合)、次の質問/回答を参照してください。


一言で言えば

あなたがしたい場合は、あなたの具体的な機能要件については、レンダリング条件付きJSFコンポーネントを使用しrendered、代わりにJSF HTMLコンポーネントに属性を特に場合#{lpc}など、コンポーネント反復JSFの現在の反復項目を表す<h:dataTable>かを<ui:repeat>

<h:someComponent rendered="#{lpc.verbose}">
    ...
</h:someComponent>

または、条件付きでJSFコンポーネントを構築(作成/追加)する場合は、JSTLを使い続けます。これnew SomeComponent()は、Java で冗長に行うよりもはるかに優れています。

<c:if test="#{lpc.verbose}">
    <h:someComponent>
        ...
    </h:someComponent>
</c:if>

以下も参照してください。


3
@アクリン:いいえ?どの程度この例
BalusC 2012

1
最初の段落を適切に長い間解釈することはできません(例は非常に明確です)。したがって、このコメントは唯一の方法として残しておきます。その段落では、私が印象にしています<ui:repeat>(「なぜならこのラインの、タグハンドラをあるJSF自身のそのノート<f:xxx><ui:xxx>...ちょうどのような」) <c:forEach>、したがって、それはで評価されたビューの構築時間(再びただ単になどのような<c:forEach>) 。その場合、との間に目に見える機能的な違いは<ui:repeat>あり<c:forEach>ませんか?私はその段落が正確に何を意味するのか理解できません:)
小さな

1
申し訳ありませんが、これ以上この投稿を汚染することはありません。私は私の注意にあなたの以前のコメントをしたではなく、この文、「ん注JSF自身のことを<f:xxx>して<ui:xxx>は延びていないタグUIComponent、タグハンドラです。」それが意味するものではしようとする試み<ui:repeat>もあるため、タグハンドラで<ui:xxx>も含まれるの<ui:repeat>?これは、それが拡張の<ui:repeat>コンポーネントの1つであることを意味<ui:xxx>しますUIComponent。したがって、それはタグハンドラではありません。(それらの一部は拡張されない場合がありますUIComponent。したがって、それらはタグハンドラです)
小さな14

2
@Shirgill:ターゲットスコープに評価値を設定する代わりに、EL式のエイリアス<c:set>scope作成しません。scope="request"代わりに試してください。これは、値をすぐに評価し(実際にビューのビルド時に)、それを要求属性として設定します(反復中に「上書き」されません)。カバーの下で、ValueExpressionオブジェクトを作成および設定します。
BalusC 2016年

1
@ K.Nicholas:カバーの下にありますClassNotFoundException。プロジェクトの実行時の依存関係が壊れています。Tomcatなどの非JavaEEサーバーを使用していて、JSTLをインストールするのを忘れた、またはJSTL 1.0とJSTL 1.1+の両方を誤って含めた可能性があります。JSTL 1.0ではパッケージがjavax.servlet.jstl.core.*あり、JSTL 1.1以降はこれになっていjavax.servlet.jsp.jstl.core.*ます。JSTLをインストールするための手がかりはここにあります:stackoverflow.com/a/4928309
BalusC '30

13

使用する

<h:panelGroup rendered="#{lpc.verbose}">
  ...
</h:panelGroup>

Thx、素晴らしい答えです。より一般的には、JSTLタグはまだ意味がありますか、それともJSF 2.0以降は非推奨と見なす必要がありますか?
1

ほとんどの場合、そうです。しかし、それらを使用することが適切な場合もあります
Bozho 2010

3
<span>タグを生成するため、h:panelGroupの使用はダーティソリューションですが、c:ifはHTMLコードに何も追加しません。h:panelGroupは、要素をグループ化するため、panelGrids内でも問題があります。
Rober2D2

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.