この講義では、これまで図8.1[検索語入力画面から検索結果表示画面へ]のような機能を持つ「図書検索プログラム」を例にとり、JSFについて解説していました。
今回は、またこの「図書検索プログラム」に戻り、新しい機能を追加してみましょう。
今回新しく追加するのは、図8.2[詳細なデータを表示]のような機能です。
図書検索アプリケーションの検索結果表示画面で、本のタイトルをクリックすると、その本の詳細なデータが出るようにします。
「リンクをクリック」することによって、詳細表示画面に遷移します。そのためには、「どの本のデータがクリックされたか」を知る必要があります。具体的には、検索結果表示画面のJSPで使われているh:dataTableで、どのJavaBeansのデータがクリックされたかをチェックするのです。
では、検索結果表示画面のJSPのソースを見てみましょう。No.13で解説した検索結果表示画面の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は、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}">
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のソースを見てみましょう。
<%@ 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コンポーネントのさまざまな設定を変更することもできます。