- PR -

JSF の結果が反映されない

1
投稿者投稿内容
まごまご
常連さん
会議室デビュー日: 2005/11/16
投稿数: 24
投稿日時: 2006-02-25 20:09
お世話になります。
現在、検索画面を作成しているのですが、
ある動作の時だけ<h:dataTable>の結果が反映されず1週間ほど悩んでいます。

・メニューから検索画面が呼ばれる。
menu.jsp -------------------------
<h:commandLink action="X01-1" value="検索画面">
 <f:param id="X01" name="init" value="true"/>
</h:commandLink>
----------------------------------

faces-config.xml -------------------------
メニューは画面上部に常に存在する為、from-view-idを * としました.
<navigation-rule>
 <from-view-id>*</from-view-id>
 <navigation-case>
  <from-outcome>X01-1</from-outcome>
  <to-view-id>/search.jsp</to-view-id>
 </navigation-case>
</navigation-rule>

検索画面にはページ処理がある為、以下のように設定しました。
<navigation-rule>
 <from-view-id>/search.jsp</from-view-id>
 <navigation-case>
  <from-outcome>search</from-outcome>
  <to-view-id>/search.jsp</to-view-id>
 </navigation-case>
</navigation-rule>
----------------------------------

・メニューから呼ばれたsearch.jspでは、マネージドビーンにsearchBeanを使用しています。
 パラメータ" init "を受け取り、コンストラクタ内で、検索メソッドを実行しています。
 マネージドビーンは、request スコープです。
searchBean -------------------------
public searchBean() {
 private String no;

 // リクエストから値を取得しています。
 HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();

 // パラメータが存在した場合は検索メソッドを呼びます。
 String init = request.getParameter("init");
 if (init != null && !init.trim().equals("")) {
  this.search();
 }

 // 検索メソッド
 public String search() {
  // 検索処理
  List result = logic.search(no);

  UIData uidata = new UIData();
  uidata.setValue(result);

  return "search";
 }
 // getter/setter
}
----------------------------------

search.jsp ----------------------------------

検索条件:<h:inputText id="no_s" value="#{searchBean.no}"/>
<br>
<h:commandButton action="#{searchBean.search}" value=" 検 索 "/>

<h:dataTable id="table"
 binding="#{f01Bean.itemtable}"
 value="#{f01Bean.results}"
 first="0"
 var="result">

<h:column>
 <h:outputText value="#{result.no}"/>
 <f:facet name="header">
  <h:outputText value="No."/>
 </f:facet>
</h:column>
<h:column>
 <h:outputText value="#{result.name}"/>
 <h:commandButton action="#{searchBean.nextA}" value=" 詳細 "/>
 <f:facet name="header">
  <h:outputText value="氏名"/>
 </f:facet>
</h:column>
<h:column>
 <h:outputText value="#{result.tel}"/>
 <h:commandButton action="#{searchBean.nextB}" value=" 詳細 "/>
 <f:facet name="header">
  <h:outputText value="電話番号"/>
 </f:facet>
</h:column>

</h:dataTable>
----------------------------------

問題になっている現象は、
メニューから初期表示(検索結果 > 0)させ
その後、ページ移動で移動した後、再度メニューのリンクを起動した時、
ページ番号や、検索条件項目などは初期化されるのですが
結果行だけが直前のページ情報のままになってしまうのです。
マネージドビーン側では最新の情報(1ページ目)を取得し、
値をセットしているのですが、画面には(1ページ目)が反映されません。

私自身正直まだJSFを理解できていないのがそもそもの原因なのですが
なんとかコンポーネントの値を初期状態にできないでしょうか。

よろしくお願いします。
まごまご
常連さん
会議室デビュー日: 2005/11/16
投稿数: 24
投稿日時: 2006-02-25 20:09
すみません。追記です。

コンポーネントをバインドさせた経緯です。
1:データ出力の際に各行の項目から、出力形式を変える為
  使用していました。

前回の投稿で、コンポーネントのバインディングは極力しない方が良い
と、ご指摘を受け改善したのですが、

2:1はバインディングが無くても実装出来ました。
  ところが
3:マネージドビーンが request スコープの為、
  コンポーネントのバインディングを外してしまうと
  画面描画の問題はクリアされたが、アクションが機能しなくなった。
  (結果行に複数個の画面遷移ボタンがあります。)

よろしくお願いします。
よしだひろゆき
大ベテラン
会議室デビュー日: 2004/11/22
投稿数: 141
投稿日時: 2006-02-27 18:55
もう少し説明願います

引用:
ページ番号や、検索条件項目などは初期化されるのですが

「ページ番号」とは?

引用:
結果行だけが直前のページ情報のままになってしまうのです。
マネージドビーン側では最新の情報(1ページ目)を取得し、
値をセットしているのですが、画面には(1ページ目)が反映されません。

「マネージドビーン側」とはどちらのマネージドビーンでしょうか?
searchBeanとf01Beanとの間でどうやってresultを渡していますか?
UIDate.setValue()というあたりが怪しいですね.

引用:
3:マネージドビーンが request スコープの為、
  コンポーネントのバインディングを外してしまうと
  画面描画の問題はクリアされたが、アクションが機能しなくなった。
  (結果行に複数個の画面遷移ボタンがあります。)

コンポーネントバインディングで無理やりAPIを駆使してプログラムでなんとかしようとする前に,
まずは典型的なサンプルを調べてそれをまねてみるべきではないでしょうか?
普通は:
・コンポーネントバインディングは使わない
・h:datatableのvalueはセッションスコープのマネージドビーンを使う
・行別のアクションではDataModel.getRowData()で該当データをもらう
よしだひろゆき
大ベテラン
会議室デビュー日: 2004/11/22
投稿数: 141
投稿日時: 2006-02-28 10:37
引用:

まずは典型的なサンプルを調べてそれをまねてみるべきではないでしょうか?

と言ったのですが,インターネット上で推奨できるサンプルがなかなか見つからないので,
自分で作ってみました:
コード:
<html>
    <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
    <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<head>
</head>
<body>
<f:view>
<h:form>
<h:dataTable var="item" value="#{myBean.items}" border="1">
	<h:column>
		<h:selectBooleanCheckbox value="#{item.checked}"/>
	</h:column>
	<h:column>
		<f:facet name="header"><h:outputText value="name"/></f:facet>
		<h:outputText value="#{item.name}"/>
	</h:column>
	<h:column>
		<f:facet name="header"><h:outputText value="price"/></f:facet>
		<h:inputText value="#{item.price}">
			<f:validateLongRange maximum="100000" minimum="0"/>
		</h:inputText>
	</h:column>
	<h:column>
		<h:commandButton value="select" action="#{myBean.select}"/>
	</h:column>
</h:dataTable>
Selected is <h:outputText value="#{myBean.selected.name}"/>
@<h:outputText value="#{myBean.selected.price}"/>.
<p><h:commandButton value="narrow" action="#{myBean.narrow}" 
		rendered="#{myBean.items.rowCount > 0}"/>
<h:commandButton value="refresh" action="#{myBean.refresh}" immediate="true"/>
</h:form>
<HR>
<h:messages layout="table"/>
</f:view>
</body>
</html>

import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
public class MyBean {
	private DataModel items;
	private List itemList;
	private MyItem selected;
	public DataModel getItems() {
		return items;
	}
	public MyItem getSelected() {
		return selected;
	}
	public String select() {
		if (items != null) {
			selected = (MyItem)items.getRowData();
		}
		return null;
	}
	public String narrow() {
		ArrayList list = new ArrayList();
		Iterator it = itemList.iterator();
		while(it.hasNext()) {
			MyItem item = (MyItem)it.next();
			if (item.isChecked()) {
				list.add(item);
			}
		}
		itemList = list;
		items = new ListDataModel(itemList);
		return null;
	}
	public String refresh() {
		itemList = new ArrayList();
		MyItem item;
		item = new MyItem("pencil", 10); itemList.add(item);
		item = new MyItem("eraser", 5); itemList.add(item);
		item = new MyItem("pen", 200); itemList.add(item);
		item = new MyItem("notebook", 100); itemList.add(item);
		item = new MyItem("diary", 1000); itemList.add(item);
		item = new MyItem("envelop", 50); itemList.add(item);
		item = new MyItem("calcurator", 1000); itemList.add(item);
		item = new MyItem("scale", 10); itemList.add(item);
		item = new MyItem("compass", 10); itemList.add(item);
		item = new MyItem("toy", 150); itemList.add(item);
		item = new MyItem("candy", 10); itemList.add(item);
		items = new ListDataModel(itemList);
		return null;
	}
}

public class MyItem {
	private String name;
	private int price;
	private boolean checked;
	public MyItem(String n, int p) {
		name = n;
		price = p;
		checked = false;
	}
	public boolean isChecked() {
		return checked;
	}
	public String getName() {
		return name;
	}
	public int getPrice() {
		return price;
	}
	public void setChecked(boolean checked) {
		this.checked = checked;
	}
	public void setName(String name) {
		this.name = name;
	}
	public void setPrice(int price) {
		this.price = price;
	}
}

まごまご
常連さん
会議室デビュー日: 2005/11/16
投稿数: 24
投稿日時: 2006-03-02 01:48
返答が遅くなり申し訳ございません。
返信頂きまして、ありがとうございます。

ご指摘、サンプル等頂きありがとうございます。

なんとか無事解決できましたので、ご報告いたします。
---------------------------------------------------------
<h:dataTable>での以下アトリビュートに設定された
binding="#{searchBean.itemtable}"
value="#{searchBean.results}"
searchBeanのitemtableのsetterメソッド内にて
最新状態のresults(検索結果)を都度、
コンポーネントバインディングされている
itemtable.setValue()でセットすることで解決出来ました。

public void setItemtable(UIData itemtable){
  this.itemtable = itemtable;
  if (results != null) {
   this.itemtable.setValue(results);
  }
}

コンストラクタ内で検索メソッドを呼んでいる為、
検索結果が最新状態であっても、コンストラクタ後に
呼び出されるsetterメソッドでUIコンポーネントの値が
上書きされていた?(ちょっと自信ないです)
---------------------------------------------------------

 引用: --------------------------------------------------------------------------------

  まずは典型的なサンプルを調べてそれをまねてみるべきではないでしょうか?
  普通は:
  ・コンポーネントバインディングは使わない
  ・h:datatableのvalueはセッションスコープのマネージドビーンを使う
  ・行別のアクションではDataModel.getRowData()で該当データをもらう
 --------------------------------------------------------------------------------

 引用: --------------------------------------------------------------------------------

  まずは典型的なサンプルを調べてそれをまねてみるべきではないでしょうか?

 --------------------------------------------------------------------------------

サンプルも色々自分でカスタマイズもしながら確認しましたが
大変勉強になりました。

一時はすべてサンプルに習って修正も考えたのですが
期間がないことと、既に同様の作り込みになっているソースが多数あり
私の担当分だけ修正するわけにもいきませんでしたので、
今回のような力技に頼ってしまうこととなりました。

 引用----------------------------------------------------------------------
 「マネージドビーン側」とはどちらのマネージドビーンでしょうか?
 searchBeanとf01Beanとの間でどうやってresultを渡していますか?
 UIDate.setValue()というあたりが怪しいですね.
 --------------------------------------------------------------------------
すみません。こちらは投稿ミスでした。
すべてsearchBeanでのやり取りとなっていました。お詫び致します。
よしだひろゆき
大ベテラン
会議室デビュー日: 2004/11/22
投稿数: 141
投稿日時: 2006-03-06 20:21
引用:

<h:dataTable>での以下アトリビュートに設定された
binding="#{searchBean.itemtable}"
value="#{searchBean.results}"
searchBeanのitemtableのsetterメソッド内にて
最新状態のresults(検索結果)を都度、
コンポーネントバインディングされている
itemtable.setValue()でセットすることで解決出来ました。
public void setItemtable(UIData itemtable){
  this.itemtable = itemtable;
  if (results != null) {
   this.itemtable.setValue(results);
  }
}
コンストラクタ内で検索メソッドを呼んでいる為、
検索結果が最新状態であっても、コンストラクタ後に
呼び出されるsetterメソッドでUIコンポーネントの値が
上書きされていた?(ちょっと自信ないです)
archBeanでのやり取りとなっていました。お詫び致します。


「なぜかは分からないけど,これで動いたから,これでいいや」という感じですね ^o^;
情報がまだ足りないので,推察になりますが,たぶんこういうことでは?
まず前提として
・searchBeanはリクエストスコープ
・searchBeanのgetItemTableは最初はnullを返す
・STATE_SAVING_METHODがserver

そうすると,以下の動きになっているはずです:
[最初のリクエストのRender Responseフェーズ(JSP実行時)]
(1)<h:datatable>のbinding="#{searchBean.itemtable}" の処理で,searchBeanをインスタンス化
(2)getItemTableを呼び出すがnullが返るので,JSFがDataTableコンポーネントを生成する
(3)生成したコンポーネントを引数にsetItemTableを呼び出す
(4)したがってthis.itemtable.setValue(results)が実行される
この結果,コンポーネントに対しては,localValueとしてresultsが設定され,かつ
valueBindingとして#{searchBean.results}が二重に設定されていることになりますが,
localValueの方が優先されるので,valueBindingは評価されずにresultsが使われます.

このリクエストが終了すると,searchBeanは解放されるのですが,resultsは
コンポーネントに設定されたままになります.そして,それはコンポーネントツリーと
ともに,セッション情報として保持されます.

[画面遷移後などにまたここの戻ってきた時]
(1)コンポーネントツリーがセッション情報から復元される
 このときにUIDataのlocalValueにさきほどのresultsが設定されたままであることに注意
 最初の質問で「ある動作の時だけ<h:dataTable>の結果が反映されず」といっているのはこのためだろうと思います
(2)searchBeanがインスタンス化され,setItemTableが呼び出される
 ここでhis.itemtable.setValue(results); が実行されるので,正常に表示される

というわけで正常に動作するように見えているわけです.

ですが,そもそもsearchBeanをセッションスコープにすることを嫌って,リクエストス
コープにしたのはなぜかというと,検索結果resultsをセッション情報に残したくなかった
からですよね.現状ではコンポーネントツリーといっしょに残っているわけです.
setItemValueの変更は「残さないようになった」のではなくて,残っているものを
次のリクエストで正しい値に設定しなおすようにしただけです.

h:datatableのvalueにつかうマネージドビーンはセッションスコープが基本です.
まごまご
常連さん
会議室デビュー日: 2005/11/16
投稿数: 24
投稿日時: 2006-03-10 10:21
大変、参考になりました。
ありがとう御座います。

 引用:----------------------------------------------------------
  「なぜかは分からないけど,これで動いたから,これでいいや」という感じですね ^o^;
  --------------------------------------------------------------

 はい。恥ずかしながら図星です。。。時間的にも結構切羽詰っていたもので、、、

 引用:----------------------------------------------------------
  そうすると,以下の動きになっているはずです:
  [最初のリクエストのRender Responseフェーズ(JSP実行時)]
  (1)<h:datatable>のbinding="#{searchBean.itemtable}" の処理で,searchBeanをインスタンス化
  (2)getItemTableを呼び出すがnullが返るので,JSFがDataTableコンポーネントを生成する
  (3)生成したコンポーネントを引数にsetItemTableを呼び出す
  (4)したがってthis.itemtable.setValue(results)が実行される
  この結果,コンポーネントに対しては,localValueとしてresultsが設定され,かつ
  valueBindingとして#{searchBean.results}が二重に設定されていることになりますが,
  localValueの方が優先されるので,valueBindingは評価されずにresultsが使われます.
  ----------------------------------------------------------

  (4)の前半部までは(なんとなくですが)推察出来たのですが
  「localValueの方が優先されるので」など全く勉強不足でした。
よしだひろゆき
大ベテラン
会議室デビュー日: 2004/11/22
投稿数: 141
投稿日時: 2006-03-10 11:41
引用:
 「localValueの方が優先されるので」など全く勉強不足でした。


ValueHolder.getValueのjavadocに書いてあります.
やはりコンポーネントバインディングにうかつに手を出さない方がよろしいのではないでしょうか?

そもそも
引用:
3:マネージドビーンが request スコープの為、
  コンポーネントのバインディングを外してしまうと
  画面描画の問題はクリアされたが、アクションが機能しなくなった。


こちらを解決するのがまっとうだろうと思います.
1

スキルアップ/キャリアアップ(JOB@IT)