列の並べ替えと書式設定
テーブル内の列を並べ替え可能にするには、その列にsortable属性を指定し、<display:column sortable="true">のようにします。これにより、列の見出しが、テーブルデータを昇順または降順に並べ替えるためのハイパーリンクになります。テーブルのHTMLが生成されるときには、列見出しのセルが、<th class="order1">(昇順の場合)または<th class="order2">(降順の場合)として出力されます。このクラス名に基づき、列見出しのCSS背景イメージとして上矢印または下矢印を追加することができます。
既定では、現在表示されているページ上のデータだけが並べ替えられます(後述の「ページナビゲーション」を参照)。この動作を変更するには、「displaytag.properties」ファイルにsort.amount=listというプロパティを追加します。サンプルアプリケーションでは、実際にこの設定を採用しています。
テーブルデータを書式設定するには、書式を適用したい列にformat="Pattern"という形式で属性を追加します。Patternの部分には、有効なjava.text.MessageFormatパターンを指定できます。例えば<display:column format="{0,date,short}">とすると、2003年11月1日という日付が「11/1/03」という書式で出力されます(実際の書式はロケール設定によって異なります)。
行のグループ化と小計の計算
DisplayTagの大きな特徴の1つは、行のグループ化と小計の計算の機能を組み込みで備えていることです。例えば、数多くの注文の明細行をデータソースから取得し、その結果を表示したいとします。このような場合によく問題になるのが、同じ情報が繰り返し表示されることです。同じ注文を構成する連続した明細行では、同じ顧客名、注文番号、注文日などが繰り返されることになります。このような状態だと、どの明細行がどの注文に関連付けられているのかが判別しにくくなります。
DisplayTagは、グループ属性を持つ列(例えば<display:column group="1">の列など)を調べ、そのいずれかの列に繰り返しデータが含まれている行をすべてグループ化します。今回のサンプルアプリケーションでは、Customer列をgroup="1"、Order Number列をgroup="2"、Order Date列をgroup="3"としているため、この3つの列の組み合わせによって行がグループ化されます(リスト1参照)。
また、テーブルにTotalTableDecoratorというテーブル装飾を指定していることに注目してください。これにより、total属性を持つ列の値がグループごとに合計され、その合計値がグループの下に独立した行として表示されます。この小計行には、DisplayTagによってtotalというCSSクラスが与えられるため、このクラスを通じて特殊なスタイルを簡単に適用することができます。こうしたテーブル装飾を指定するには、<display:table decorator="org.displaytag.decorator.TotalTableDecorator">とします。また、列の小計を有効にするには<display:column total="true">とします。
ページナビゲーション
DisplayTagでは、<display:table pagesize="16">のようにpagesize属性を指定することで、テーブルにページナビゲーションを追加することができます。ページナビゲーションの際に遭遇し得るさまざまな状況(例えばデータが1ページ分しかない場合や、複数ページあるうちの先頭、中間、最終ページを表示する場合など)に対処できるようにするために、DisplayTagでは、paging.banner.onepage、paging.banner.first、paging.banner.fullのようなプロパティが用意されています(リスト3参照)。
これらのプロパティの使い方を示すために、具体的な例を1つ紹介しておきます。
paging.banner.full=<div class="pagelinks" align="right"><a href={1}>
<img src="images/first.gif"></a><a href={2}>
<img src="images/prev.gif"></a>{0}<a href={3}>
<img src="images/next.gif"></a><a href={4}>
<img src="images/last.gif"></a></div>
このやや複雑なプロパティは、すべてのページングリンクを表示するときに、図2のようなバナーを出力することをDisplayTagに指示しています。

ここでは、<div>を使用することで、ページングバナーにpagelinksというCSSスタイルを割り当てています。{1}は、データの先頭ページへのリンクを表すプレースホルダです。ここでは、クリッカブルイメージのターゲットURLとして使用されています。{2}、{3}、{4}は、それぞれ前ページ、次ページ、最終ページについてのプレースホルダです。{0}は、一連の番号付きページへのリンクを出力する特殊なプレースホルダです。
Excel、PDFなどへのエクスポート
DisplayTagで表現したテーブルのデータをCSV(Comma Separated Value)、XML、Excel、PDF、RTFにエクスポートするには、テーブル属性を1つ追加し、プロパティをいくつか設定し、それぞれのエクスポートタイプについて<span>スタイルを作成する必要があります。
まず、テーブルにexport属性を追加すると、DisplayTagによってエクスポート用のバナーが表示されます。例えば、<display:table export="true">のようにします。
ページナビゲーションのときと同様に、DisplayTagにはエクスポート関連のプロパティが数多く用意されています。既定のプロパティは「displaytag.properties」ファイルで設定します。サポートされているすべての形式をユーザーが使用できるようにする場合は、通常はexport.bannerプロパティとexport.format.filenameプロパティを設定します。
例えば、今回のサンプルアプリケーションでは、エクスポート形式のリスト(エクスポートバナー)を右端に配置し、このリストにpagelinksというCSSクラスを適用したいので、「displaytag.properties」ファイルに次のように記述しています(リスト3参照)。
export.banner=<div class="pagelinks" align="right">{0}</div>
この設定だけを見ると少々分かりにくいのですが、エクスポートバナーが描画されるときには、個々のエクスポート形式のハイパーリンクが「export format」クラスの<span>スタイル(例えば<span class="export excel">など)の中に挿入されます。そのため、サンプルアプリケーションでは、それぞれの形式に対応するCSSクラスを次のような形で用意しています。
span.excel {
background-image: url(../images/ico_file_excel.png);
background-repeat: no-repeat;
width: 16px;
}
エクスポートしたデータのファイル名を設定するには、export.format.filenameプロパティをexport.pdf.filename=data.pdfのように指定します。
Javascriptによる行操作
今回のサンプルアプリケーションのもう1つの要件は、ユーザーが行をマウスポインタで指すとその行が強調表示され、行のどこかをクリックすると、その行についての識別情報を含んだ新しい要求が生成されるようにすることです。この要件を満たすには、何らかのJavaScriptを取り入れる必要があります。
サンプルアプリケーションでは、「RowHandlers.js」というJavaScriptファイルを用意し(リスト4参照)、その中にaddRowHandlers()という関数を作成しました。この関数は、HTMLテーブル内の各行に次の3つのイベントハンドラを追加します。
- onmouseover ―― 行のクラス属性を保存し、別の背景色または画像を持つ新しいスタイルへと変更します。
- onmouseout ―― 以前のクラス属性を復元します。
- onclick ―― 指定のURLにジャンプします。このとき、選択されたテーブル行に関する要求パラメータをURLに付加します。
// Adds onmouseover, onmouseout, and onclick handlers // to each table row. // The onmouseover handler changes the row's class attribute to // rowMouseOver. The onmouseout handler changes it back. // The onclick function makes a request for the specified url, // including the innerHTML of the specified column // as a request parameter. function addRowHandlers(tableId, rowClassName, url, paramName, columnIndex) { var previousClass = null; var table = document.getElementById(tableId); var rows = table.getElementsByTagName("tr"); for (i = 1; i < rows.length; i++) { rows[i].onmouseover = function () { previousClass = this.className; this.className = this.className + " " + rowClassName ; }; rows[i].onmouseout = function () { this.className = previousClass; }; rows[i].onclick = function () { var cell = this.getElementsByTagName("td")[columnIndex]; var paramValue = cell.innerHTML; location.href = url + "?" + paramName + "=" + paramValue; }; } }
この「RowHandlers.js」を自作のJSPで利用するには、次のようにします。
<head>セクション内でこのスクリプトにリンクします。<body>タグのonload属性でaddRowHandlers()を呼び出します。
<script src="js/RowHandlers.js" language="javascript" type="text/javascript" /></script>
<body onload= "addRowHandlers('row', 'rowMouseOver',
'OrderDetail.jsp', 'id', 0)">
手順2のタグの意味を解釈してみましょう。まず、ページの<body>セクションがロードされるときに、rowというidを持つテーブルに対してaddRowHandlers()関数が呼び出され、この関数によってテーブルの各行に3つのハンドラが追加されます。これらのハンドラが適用された場合、ユーザーがいずれかの行をマウスポインタで指すと、その行のCSSクラス属性がrowMouseOverに変更されます。マウスポインタが行から外れると、CSSクラス属性は元の値に戻ります。ユーザーが行をクリックすると「OrderDetail.jsp」が呼び出されますが、この呼び出し要求にはパラメータidの値が付加されます(この値は、クリックされた行の列0から取得されます)。
サンプルアプリケーションでは、「OrderDetails.jsp」(リスト1参照)が前述の「RowHandlers.js」を使用しています。また、「OrderDetails.jsp」では、行IDの値を非表示の列に配置するという手法を使っています。この列を非表示にするために、<display:column>のclassおよびheaderClass属性をhiddenに設定しています(hiddenは、displayプロパティがnoneに設定されているCSSスタイルです)。これは、要求スコープで使用するデータをユーザーの目から隠したいときに役立つ、単純ながら効果的な方法です。
DisplayTagは、JSP内にテーブルデータを簡単に表示したいときに役立つオープンソースのタグライブラリです。このライブラリは幅広い用途に利用でき、例えば検索アプリケーションでは検索結果を特定の列で並べ替え、そこから製品リストにジャンプさせるページナビゲーションを実現できますし、財務レポートアプリケーションでは数値の書式設定、グループ集計、PDFへのエクスポートの機能を利用できます。可能性は無限にあり、おそらく読者の皆さんが開発しているWebアプリケーションでも、DisplayTagがお役に立つでしょう。
まずサンプルアプリケーションをダウンロードし、実際に試してみてください。その後、「OrderDetails.jsp」をテンプレートとして使い、目的に合ったダイナミックテーブルを作成してみましょう。ダウンロードサンプルに収録されているCSSファイル、JavaScriptファイル、プロパティファイル、画像ファイルは、そのままの形で利用しても、個々のアプリケーションに合わせて必要に応じて改変してもかまいません。

'OrderDetail.jsp', 'id', 0)">