Powered by SmartDoc

Component Binding

図書検索プログラム (2)

この講義では、これまで図9.1[検索語入力画面から検索結果表示画面へ]のような機能を持つ「図書検索プログラム」を例にとり、JSFについて解説していました。

検索語入力画面から検索結果表示画面へ

今回は、またこの「図書検索プログラム」に戻り、新しい機能を追加してみましょう。

新しく追加する機能

今回新しく追加するのは、図9.2[詳細なデータを表示]のような機能です。

詳細なデータを表示

図書検索アプリケーションの検索結果表示画面で、本のタイトルをクリックすると、その本の詳細なデータが出るようにします。

「リンクをクリック」することによって、詳細表示画面に遷移します。そのためには、「どの本のデータがクリックされたか」を知る必要があります。具体的には、検索結果表示画面のJSPで使われているh:dataTableで、どのJavaBeansのデータがクリックされたかをチェックするのです。

h:commandLink タグ

では、検索結果表示画面のJSPのソースを見てみましょう。No.13で解説した検索結果表示画面のJSPに、タグを一つ追加しています。

list.jsp
<%@ page contentType="text/html; charset=Shift_JIS" %>
<%@ taglib uri="http://java.sun.com/jsf/html"  prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core"  prefix="f" %>

<html>
<head>
<title>検索結果</title>
</head>
<body>

<h1>検索結果</h1>

<f:view>
<h:form id="listForm">
<h:dataTable id="table" border="1"
              value="#{BookSearcher.bookList}" var="book"
              binding="#{BookSearcher.bookDataTable}">

    <h:column>
        <f:facet name="header">
            <h:outputText  value="タイトル"/>
        </f:facet>
        <h:commandLink id="detail"
                   action="#{BookSearcher.searchBookDetail}">
            <h:outputText id="bookTitle" value="#{book.title}"/>
        </h:commandLink> 
    </h:column>
    <h:column>
        <f:facet name="header">
            <h:outputText  value="著者"/>
        </f:facet>
        <h:outputText id="bookAuthor" value="#{book.author}"/>
    </h:column>
</h:dataTable>
</h:form>
</f:view>

</body>
</html>

本のタイトルを表示するh:outputTextタグを、h:commandLinkタグで囲っています。ここが前回との違いです。

これまでは、h:commandButtonタグを用いて、ボタンが押されたら画面を遷移させることができました。ボタンではなく、ハイパーリンクを用いて画面遷移を行うときには、h:commandLinkタグを利用します。

h:commandLinkタグには、ほかのUIコンポーネントと同様に、id属性があります。また、h:commandButtonタグと同様に、action属性でAction Methodを指定できます。

h:outputText要素で指定された文字列をクリックすると、Action Methodのoutcomeとfaces-config.xmlの設定によって画面が遷移します。

        <h:commandLink id="detail"
                   action="#{BookSearcher.searchBookDetail}">
            <h:outputText id="bookTitle" value="#{book.title}"/>
        </h:commandLink> 

何が問題になるのか?

検索結果表示画面で、ハイパーリンクになっているある本のタイトルがクリックされたら、Action Methodが実行されてその本に関する詳細なデータが調べられ、詳細表示画面に遷移します。

ここで、「その本に関する詳細なデータが調べられ」るためには、検索結果表示画面でどの本のデータがクリックされたのか、Action Methodが知っている必要があるのです。

もちろん、検索語入力画面で入力された検索語によって、検索結果表示画面の出力結果は動的に変化します。つまり、どの本のデータがクリックされたのか、Action Methodで動的に調べる方法が必要になります。

ここで、以前にも解説した、検索結果表示画面で本のデータの表示に利用されるh:dataTableタグの部分を見てみましょう。

<h:dataTable id="table" border="1"
             value="#{BookSearcher.bookList}" var="book"
    ......
</h:dataTable>

h:dataTableは、java.util.Listやjava.util.Iterator,あるいは配列などのコレクションに含まれるJavaBeansのデータを、表形式で表示するUIコンポーネントでした。表示するJavaBeansのコレクションは、h:dataTableのvalue属性で指定します。

h:dataTableのvar属性で指定された"book"は、コレクション中の1つの要素を表す変数名になります。今回の例では、"bookList"というjava.util.List型のコレクションの各要素は、"BookData"という型になっています。

h:dataTableでは、表の1行のデータが1つのJavaBeansに対応します。ここでは、表の1行のデータは、h:dataTableのvar属性で指定された"book"という名前を持つ、BookData型のJavaBeansとなるのです。

Component Binding

「どの本がクリックされたか」調べるにはいくつか方法がありますが、ここでは"Component Binding"のしくみから調べることにしましょう。

Component Bindingは、UIコンポーネントとManaged Beanを結びつけるしくみです。

まず、h:dataTableの内部状態を調べるために、Managed BeanであるBookSearcher.javaにUIData型のプロパティを用意します。JSFの内部では、dataTableはUIDataという型を持っているからです。

public UIData getBookDataTable{ … };
public void setBookDataTable(UIData d) {};

次に、h:dataTableのbinding属性を使って、bookDataTableプロパティとh:dataTableを結びつけます。これで、dataTableの内部状態を調べることができるようになります。

<h:dataTable id="table" border="1"
              value="#{BookSearcher.bookList}" var="book"
              binding="#{BookSearcher.bookDataTable}">

Action Method の実装

BookSearcher.java
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.sql.*;
import javax.sql.*;
import javax.faces.component.UIData;

public class BookSearcher {

	private UIData bookDataTable;
	private String word = "";
	private String id   = "";
	private List list = null;
	private BookData book = null;

	public void setWord(String word) {
		this.word = word;
	}

	public String getWord() {
		return word;
	}

	public List getBookList() {
		return list;
	}

	public BookData getBookData() {
		return book;
	}

	public void setBookDataTable(UIData data) {
		bookDataTable = data;
	}

	public UIData getBookDataTable() {
		return bookDataTable;
	}

	public String searchBooks() {
		searchBooks(word);
		return "success";
	}

	public String searchBookDetail() {
		book = (BookData)bookDataTable.getRowData();
		return "detail";
	}

	private void searchBooks(String word) {
		list = new ArrayList();
		try {
			Class.forName("org.hsqldb.jdbcDriver");
			String url = "jdbc:hsqldb:hsql://localhost";
			Connection con = DriverManager.getConnection(url, "sa", "");

			String selectStatement =
				"select * " +
				"from books where ndc           like ? " +
				              "or tyosya_hyouji like ? " +
				              "or id            like ? " +
				              "or title         like ? " +
				              "or author        like ? " +
				              "or publisher     like ? ";
			PreparedStatement prepStmt =
				con.prepareStatement(selectStatement);
			prepStmt.setString(1, appendPercent(word));
			prepStmt.setString(2, appendPercent(word));
			prepStmt.setString(3, appendPercent(word));
			prepStmt.setString(4, appendPercent(word));
			prepStmt.setString(5, appendPercent(word));
			prepStmt.setString(6, appendPercent(word));

			ResultSet rs = prepStmt.executeQuery();
			while (rs.next()) {
				BookData book = new BookData();
				book.setNdc(rs.getString("ndc"));
				book.setTyosya_hyouji(rs.getString("tyosya_hyouji"));
				book.setId(rs.getString("id"));
				book.setTitle(rs.getString("title"));
				book.setAuthor(rs.getString("author"));
				book.setPublisher(rs.getString("publisher"));
				list.add(book);
			}
			prepStmt.close();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	private String appendPercent(String from) {
		StringBuffer to = new StringBuffer();
		to.append("%");
		to.append(from);
		to.append("%");

		return new String(to);
	}
}

searchBookDetailメソッドが、詳細表示を出力する準備を行うAction Methodになります。非常に単純ですね。

Component BindingされたUIData型のbookDataTableから、クリックされた行のbookインスタンスを取得できるのです。

	public String searchBookDetail() {
		book = (BookData)bookDataTable.getRowData();
		return "detail";
	}

取得したデータは、このBookSearcherクラスのbookフィールドに格納されます。このクラス内に、bookフィールドの値を返すgetBookDataというメソッドが存在することから、bookDataプロパティに格納された、と言うこともできるでしょう。

詳細表示のJSP

では、詳細表示を行うJSPのソースを見てみましょう。

detail.jsp
<%@ page contentType="text/html; charset=Shift_JIS" %>
<%@ taglib uri="http://java.sun.com/jsf/html"  prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core"  prefix="f" %>

<html>
<head>
<title>詳細表示</title>
</head>
<body>

<h1>詳細表示</h1>

<f:view>
<table border="1">

<tr>
<td>NDC</td>
<td><h:outputText value="#{BookSearcher.bookData.ndc}" /></td>
</tr>

<tr>
<td>著者表示</td>
<td><h:outputText value="#{BookSearcher.bookData.tyosya_hyouji}" /></td>
</tr>

<tr>
<td>ID</td>
<td><h:outputText value="#{BookSearcher.bookData.id}" /></td>
</tr>

<tr>
<td>タイトル</td>
<td><h:outputText value="#{BookSearcher.bookData.title}" /></td>
</tr>

<tr>
<td>著者</td>
<td><h:outputText value="#{BookSearcher.bookData.author}" /></td>
</tr>

<tr>
<td>出版社</td>
<td><h:outputText value="#{BookSearcher.bookData.publisher}" /></td>
</tr>

</table>
</f:view>

</body>
</html>

次の部分では、BookSearcher BeanのbookDataプロパティのidプロパティを表示していることになります。

<li><h:outputText value="#{BookSearcher.bookData.id}" />

このようにして、詳細表示画面の出力を行っているのです。

今回のまとめ

今回のようなアプリケーションでは、h:dataTable中のどのデータがクリックされたのかをAction Methodで調べる必要があります。

そこで、Managed Beanにh:dataTableに対応するオブジェクトであるUIData型のプロパティを用意します。そして、Component Bindingによって、このプロパティとh:dataTableを明示的に結びつけます。

このしくみによって、h:dataTableに対してどのような操作が行われたか、Component BindingされているUIData型のプロパティを通して調べることができるのです。

今回のプログラムでは、こうしてクリックされた行に対応するBookDataオブジェクトを取得できました。

このComponent Bindingのしくみによって、Action MethodからUIコンポーネントの内部情報を取得するだけでなく、UIコンポーネントのさまざまな設定を変更することもできます。