この講義では、これまで図9.1[検索語入力画面から検索結果表示画面へ]のような機能を持つ「図書検索プログラム」を例にとり、JSFについて解説していました。
今回は、またこの「図書検索プログラム」に戻り、新しい機能を追加してみましょう。
今回新しく追加するのは、図9.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コンポーネントのさまざまな設定を変更することもできます。