JavaのWebアプリケーションフレームワークであるJSFを仕事で使う機会があったので、
備忘録を残そうと思います。
- 使用環境
- NetBeans8.2
- JavaEE7.0
- GlassFish4.1.1
JSFのスコープについて
簡単なサンプルを基に今回の備忘録を書きます。
【ManagedBean】
import java.util.ArrayList;
import java.util.List;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
@Named(value = "managedBean")
@ApplicationScoped
public class ManagedBean {
private String selectedItem;
private List<String> strList;
public void setStr(){
strList = new ArrayList<>();
strList.add("test1");
strList.add("test2");
strList.add("test3");
strList.add("test4");
}
public String getSelectedItem() {
return selectedItem;
}
public void setSelectedItem(String selectedItem) {
this.selectedItem = selectedItem;
}
public List<String> getStrList() {
return strList;
}
public void setStrList(List<String> strList) {
this.strList = strList;
}
}【view】
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<f:metadata>
<f:viewAction action="#{managedBean.setStr()}" />
</f:metadata>
<h:head>
<title>Scope Test</title>
</h:head>
<h:body>
<h:form>
<ui:repeat value="#{managedBean.strList}" var="item" >
<h:commandButton value="#{item}">
<f:setPropertyActionListener target="#{managedBean.selectedItem}" value="#{item}" />
</h:commandButton>
</ui:repeat>
<br/>
<h:outputText value="#{managedBean.selectedItem}" />
</h:form>
</h:body>
</html>
【実行例】
【ボタンクリック後】
サンプルの処理をまとめると
・f:metadata内のf:viewActionタグに設定された、ManagedBeanクラスのsetStrメソッドを実行
・画面表示
・いずれかのボタンをクリックした時にf:setPropertyActionListenerがManagedBeanクラスの変数selectdItemに値を設定
・変数selectdItemをテキストで表示
以上のすごくシンプルな内容です。
今回の主題はここから。
ManagedBeanのスコープをRequestScopedに変更
先ほどのサンプルはApplicationScopedに設定していました。
名前の表すとおりアプリケーションの稼働中有効なスコープ範囲です。
これを下記のようにRequestScopedに変更します。
RequestScopedに設定すると一度のリクエストレスポンスのやり取りでスコープの有効範囲は切れてしまいます。
【ManagedBean】
import java.util.ArrayList;
import java.util.List;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
@Named(value = "managedBean")
@RequestScoped
public class ManagedBean {
private String selectedItem;
private List<String> strList;
public void setStr(){
strList = new ArrayList<>();
strList.add("test1");
strList.add("test2");
strList.add("test3");
strList.add("test4");
}
public String getSelectedItem() {
return selectedItem;
}
public void setSelectedItem(String selectedItem) {
this.selectedItem = selectedItem;
}
public List<String> getStrList() {
return strList;
}
public void setStrList(List<String> strList) {
this.strList = strList;
}
}【view】
変更なし
【実行例】
【ボタンクリック後】
今度はボタンをクリックすると画面表示が消えて真っ白になりました。
これらの原因は以下のとおりです。
・ボタンが表示されなくなる原因
ボタン表示はui:repeatタグを使用して、ManagedBeanクラスのコレクション変数であるstrListの要素数だけ繰り返します。
しかしながら、strListに要素を追加するメソッドの呼び出しをf:viewActionタグで行なっていると、こちらはGETリクエストのみに反応してくれるタグなので、ボタンクリックなどのPOSTリクエストに対してはメソッドは呼ばれません。
結果、最初の表示に使った値はスコープの有効範囲が切れており、値を設定するメソッドも呼ばれていないのでボタンが形成されません。
・クリックしたボタンの名前が表示されなくなる原因
こちらの表示方法はシンプルでManagedBeanクラスの変数selectedItemの値を取得しているだけです。
そしてそのselectedItemに値を設定するのが以下のプログラム
<ui:repeat value="#{managedBean.strList}" var="item" >
<h:commandButton value="#{item}">
<f:setPropertyActionListener target="#{managedBean.selectedItem}" value="#{item}" />
</h:commandButton>
</ui:repeat>
この中でもボタンクリックで実行されるのが、先ほども触れたこの1行。
f:setPropertyActionListener target=”#{managedBean.selectedItem}” value=”#{item}”
このvalue属性に使用しているEL式が値設定の時に、再度インスタンスを参照しにいくのですが、スコープの有効期限が切れているため値の設定に失敗して真っ白といった流れになります。
つまりRequestScopedに設定する場合は、表示するだけならいいですが値の送信を行う場合は少し工夫しないといけないということです。
画面のHTMLにはしっかり文字列形成出来ているので、その値を使ってくれって思ってしまうのですが。。。
自分はフレームワークの都合上RequestScopedでせざるを得なかったので、値の送信にはonclickイベントでJavaScriptを使って行いました。
今回はここまでです。


コメント