1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

jsfで<c:forEach>が<f:event type="preRenderView">の前に実行されてしまう

Last updated at Posted at 2016-02-05

環境

javaEE 6、jsf2.1

問題

test.xhtml
<!-- 前処理 -->
<f:event type="preRenderView" listener="#{xxx.init}">

<!-- 前処理でフィルタしたyyy.itemsに対して繰り返し処理を実施 -->
<c:forEach var="item" items="#{yyy.items}">

<c:forEach>の結果が期待通りにならない。
前処理の結果が反映されていない状態で<c:forEach>が実行されている。

原因

<c:forEach>などのタグハンドラはコンポーネントツリーのビルドやレンダリングの前に実行される。
そのためpreRenderViewの前に<c:forEach>が評価されてしまっているのが原因。

タグハンドラとコンポーネントの処理タイミングの違いは下記サイトが分かりやすい。
http://www.ninthavenue.com.au/jsf-c-foreach-vs-ui-repeat

タグハンドラはコンポーネントではなく、コンポーネントツリーを作るのを補助するのが役目なので、
コンポーネントをビルドしたりレンダリングする前に動く必要があるらしい。

対応方法1

<c:forEach>ではなく<ui:repeat>を利用する

test.xhtml
<c:forEach var="item" items="#{yyy.items}"><ui:repeat var="item" value="#{yyy.items}">

対応方法2

前処理をpreRenderViewではなく、タグハンドラの評価タイミングで実施する

直接xhtmlにEL式を記述する。(あまり良い方法ではないと思うが)

test.xhtml
<f:event type="preRenderView" listener="#{xxx.init}">
↓
# {xxx.init()}

ちなみにタグハンドラから前処理を呼ぶ方法は色々試してみたが、うまく動作しなかった。
詳細は後述。

うまくいかなかった対応1

<f:event>のpreRenderView以外のフェーズで前処理実施

<f:event>で指定できる各種フェーズで実行してみた結果。

  • preValidate:動作しない
  • postValidate:動作しない
  • postAddToView:タグハンドラの実行後に動作
  • preRenderComponent:タグハンドラの実行後に動作
  • preRenderView:タグハンドラの実行後に動作

<f:event>で指定できるフェーズ一覧。
https://javaserverfaces.java.net/docs/2.1/vdldocs/facelets/f/event.html

おそらく、コンポーネントツリーをビルドするのが「Render Response」フェーズが初めてなので、
そのタイミングで「Apply Request Values」や「Process Validations」フェーズにリスナーを仕込むことはできないのだと思う。

ちなみに一度開いた画面でsubmitした場合は、すべてのケースで期待されるフェーズで動作していた。

前回の「Render Response」フェーズで既にコンポーネントツリーを作成しているので、
「Apply Request Values」や「Process Validations」フェーズにリスナーを仕込むことができたのだと思う。

厳密にどのフェーズで実行されるのが正しいかは下記を参考に。
http://stackoverflow.com/questions/13999099/jsf-execution-order-of-fevents

うまくいかなかった対応2

<f:event type="preRenderView">ではなく、<f:viewAction>を利用する

Java EE7、jsf2.2以降なら<f:viewAction>が利用できる。
ただし、動作検証していないが、<f:event>でフェーズ指定しても正しく動かなかったのと同じ理由で<f:viewAction>もダメな気がする。

ただし、下記のコメントではできたと報告があるので要検証。
http://kikutaro777.hatenablog.com/entry/20121210/1355146339

うまくいかなかった対応3

タグハンドラで前処理を実施

<c:set>などのタグハンドラや、<f:viewParam>で試してみたがうまく動作しなかった。

test.xhtml
<f:event type="preRenderView" listener="#{xxx.init}">
↓
// 動かない
<c:set var="test" value="#{xxx.init()}" />

// 動かない
<f:viewParam id="id" name="name" value="#{xxx.init()}" />

参考)<f:viewParam>を使った方法
http://stackoverflow.com/questions/8156351/jsf-method-of-fevent-prerenderview-called-after-cforeach

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?