Ajax (Asynchronous JavaScript + XML) と JSON (JavaScript Serialized Object Notation) は次世代の Web サイトを実現可能にする 2 つの重要な技術です。ビジネス分野のアプリケーションはこの 2 つの技術を利用することによって、より直観的で応答性に優れたユーザー・インターフェースを提供することができます。この記事では、Ajax ベースの再利用可能な JSP (JavaServer Pages) TagLib コントロールを作成することによって、Ajax と JSON を Java EE (Java™ Platform, Enterprise Edition) Web アプリケーションに追加する方法を説明します。
この記事では、ユーザー・イベントに基づいてサーバーから非同期かつ動的にコンテンツを取得する更新パネル・コントロールを作成する方法を学びます。更新パネルのコンテンツは、例えばユーザーがフィールドをクリックしたとき、フィールドに値を入力したとき、あるいはドロップダウン選択リストから値を選択したときに取得されるようにします。さらに、サーバーから非同期でコンテンツを取得して表示できるポップアップ・ダイアログ・コントロールの作成方法についても説明します。このポップアップ・ダイアログが表示されるのは、例えばユーザーが項目にマウス・カーソルを重ねたとき、ボタンをクリックしたとき、ドロップダウン選択リストから値を選択したときです。
この記事は、J2EE アプリケーションを対象とした一連の Ajax 対応コントロールについて取り上げた連載の一部です。JSP TagLib コントロールを使用して作成するこれらのコントロールは、JSON、Servlet、および JavaScript 技術を利用します。
この記事では、以下の 2 つのコントロールを作成する方法を説明します。
- 更新パネル – 非同期にコンテンツを取得して、動的なユーザー・インターフェースを表示します。このコントロールでは、コンテンツの取得中および取得後に、CSS (Cascading Style Sheets) フォーマットを設定するための構成可能パラメーターを提供します。また標準イベント・ハンドラーを提供し (つまり、コンテンツがロードされる前、コンテンツがロードされた後のイベント・ハンドラー)、さらにカスタマイズできるようにします。
- ポップアップ・ダイアログ – このコントロールが提供するウィンドウは、ダイアログが初めて表示されるときにコンテンツを非同期で取得します。このポップアップ・ダイアログは必要に応じてコンテンツを取得するので、応答性に優れたユーザー・インターフェースとなります。このコントロールには CSS フォーマットを設定するための構成可能なオプション・パラメーターとして、ダイアログのタイトル、ウィンドウの右上端に配置される閉じるボタン (標準 Windows® ダイアログと同様)、そしてダイアログのサイズと位置に関するパラメーターがあります。
上記の JSP TagLib コントロールはどちらも、非同期通信、JavaScript コード、CSS フォーマット設定、そして HTML (Hypertext Markup Language) 生成をすべてカプセル化します。
この記事は、連載第 1 回と第 2 回の記事で概説した設計上の原則とコードを基に作成されています (「参考文献」を参照)。今回は、カスタム・コントロールにイベント・ハンドラーを追加するための手法、さらにコントロールの表示と振る舞いを構成するための高度な手法を紹介します。
連載のこれまでの記事と同じく、この記事で開発する JSP Ajax対応のコントロール・スイートでは、以下の内容を設計上の主な目標とします。
- 既存の Web アプリケーションに簡単に統合できるようにすること。デプロイメント・プロセスを簡易化するために、コントロールがすべてのロジックと JavaScript コードをカプセル化します。
- 簡単に構成できるようにすること。
- データ・サイズとページ・サイズのオーバーヘッドを最小限に抑えること。
- CSS および HTML 標準を使用すること。
- クロスブラウザー対応 (Windows Internet Explorer、Mozilla Firefox) であること。
- 共通デザイン・パターン/ベスト・プラクティスを適用してコードを保守しやすくすること。
これまでの 2 回の記事と同じく、この記事でも、<ajax:page/> コントロールを使用して共通 JavaScript 関数をカプセル化し、データとオーバーヘッドを最小限にします。また、クロスブラウザー対応にするために CSS および HTML などの Web 標準を使用します。コントロールによって出力される JavaScript コード、HTML、CSS は、Windows Internet Explorer 7.x と Mozilla Firefox 2.x/3.x でテストします。
この記事のサンプルを構成するクラスは、DAL (Data Abstract Layer: データ抽象層)、DTO (Data Transfer Object: データ転送オブジェクト)、BLL (Business Logic Layer: ビジネス・ロジック層)、プレゼンテーション層、そしてサポート・ヘルパーです。また、連載第 1 回の記事で使用した LocationDataService DAL クラスも使用します。これは LocationDTO DTO インスタンスのコレクションを返すクラスです。DAL または DTO クラスについては、この記事で新規に作成する必要はありません。BLL を構成するのは、Ajax 対応コントロールにデータを提供する値プロバイダーと、HTML フォームのデータに対するサーバー検証を行うサーバー・サイドのバリデーターです。
この記事では、連載第 1 回の LocationService クラスと JdbcQuery ヘルパー・クラスを使用します。これ以外に必要なユーティリティー・クラスはありません。
作成するコントロールは 2 つだけですが、この 2 つを堅牢かつ柔軟な構成可能パラメーターにするためには、複数のタグを作成しなければなりません。まずは、図 1 の UML (Unified Modeling Language) クラス図に示す更新パネル・コントロールから説明します。これは、<ajax:updatepanel/> タグの実装クラスです。
図 1. UpdatePanelTag の UML クラス図
更新パネル・コントロールがサポートするオプションの引数は、更新パネルの非同期リクエストと一緒に渡されます。<ajax:panelargument/> タグの実装クラスは、図 2 のとおりです。
図 2. PanelArgumentTag の UML クラス図
ポップアップ・コントロールはさらに複雑で、複数のタグを使用します。そのうち最も重要なタグは <ajax:popupdialog/> です。図 3 に、このタグの実装クラスを示します。
図 3. PopupDialogTag の UML クラス図
<ajax:popupdialog/> には、ダイアログ・コンテンツの非同期リクエストと一緒に渡される引数として <ajax:popuparguments/> タグを含めることができます。図 4 に、<ajax:popuparguments/> タグと <ajax:popupargument/> タグそれぞれの実装クラスを示します。
図 4. PopupDialogButtonContainerTag および PopupDialogButtonTag の UML クラス図
<ajax:popupdialog/> には、ポップアップ・ダイアログに表示されるボタンに対応する <ajax:popupbuttoncontainer/> タグを含めることができます。図 5 に、<ajax:popupbuttoncontainer/> タグと <ajax:popupbutton/> タグの実装クラスを示します。
図 5. PopupDialogArgumentContainerTag および PopupDialogArgumentTag の UML クラス図
これまで Web 開発者は、ユーザーがフィールドに値を入力する、ドロップダウン・リストから値を選択する、チェック・ボックスにチェック・マークを付ける、あるいはコントロールクリックするなどのユーザー・アクションの結果としてページの一部だけを変更する場合でも、ページ全体を更新しなければなりませんでした。コンテンツの要件とビジネス要件はそれぞれに異なるものの、ビジネス分野のほとんどのアプリケーションには、ユーザー・イベントに応じて変わってくる動的コンテンツがあります。
<ajax:updatepanel/> コントロールでの主な目的は、上記の共通シナリオに対応し、ページの変更される部分だけを簡単に動的に更新できるようにする、再利用可能なコントロールを作成することです。このコントロールは通信とプレゼンテーションの関数をすべて処理し、コンテンツの部分は Web 開発者に任せます。また、非同期リクエストを行うときに、フォームのフィールド値などの引数をオプションで渡すこともできます。柔軟性を最大限にするため、この更新パネルの表示/非表示は、その内部構造を知らなくても簡単に切り替えられるようにしなければなりません。
更新中の更新パネルは、図 6 のように表示されます。
図 6. コンテンツ取得中の更新パネル
更新パネルのコンテンツは、サーバーが非同期リクエストに応答すると更新されます。コンテンツが非同期で取得された後、更新パネルにはコンテンツが図 7 のように表示されます。
図 7. コンテンツ取得後の更新パネル
ページが初めてレンダリングされるときには、ダイアログ・ボックスの DIV コンテナーがレンダリングされます。初期の状態では、更新パネルの要素は空です。更新パネルを表示するイベントがトリガーされると、サーバーからコンテンツが非同期で取得され、更新パネルが動的コンテンツで更新されます。
表 1 に、<ajax:updatepanel/> が提供する構成可能オプションを記載します。
表 1. 更新パネルの構成可能オプション
| オプション | 内容 |
|---|---|
| id | コントロールの ID |
| url | 更新パネルのコンテンツを取得するための URL |
| updatemessage | コンテンツが非同期で取得されている間に表示するメッセージ |
| loadingcss | コンテンツのロード中に適用する CSS クラス名 (例えば、「お待ちください...」というメッセージ) |
| cssclass | コンテンツがロードされた後に適用する CSS クラス名 |
| onclear | 更新パネルをクリアする前に呼び出す JavaScript 関数のオプション名 |
| oncleared | 更新パネルをクリアした後に呼び出す JavaScript 関数のオプション名 |
| onload | 更新パネルをロードする前に呼び出す JavaScript 関数のオプション名 |
| onloaded | 更新パネルをロードした後に呼び出す JavaScript 関数のオプション名 |
イベント・ハンドラーは、このコントロールの強力な機能です。このフックを提供し、呼び出すコードは至って単純明快なものですが、これらのフックがコントロールを極めて柔軟に、再利用可能にするメカニズムとなります。
onload イベント・ハンドラーを使用すると、その特定のフィールド (更新パネルが依存するフィールド) が入力されているかどうかをテストすることができます。必須フィールドが入力されていない場合にはユーザーに対してメッセージを表示し、false を返してパネルの更新をキャンセルすることができます。この関数のもう 1 つの使用方法は、隠し INPUT 要素の値を別のフォーム・フィールドの値に基づいて入力することです。例えばイベント・ハンドラー内では、計算を行ったり、ストリングを連結し、データとして非同期リクエストと一緒に送信したりすることができます。
onloaded イベント・ハンドラーは、サーバー・レスポンスに基づいて他のフォーム・フィールドを処理するために使用することができます。例えば、サーバー・レスポンスに基づいて特定のフィールドを無効にしたり、非表示にしたり、あるいはフィールドに入力したりするなどの処理です。
更新パネルをクリアする前に特定の基準をチェックするには、onclear イベント・ハンドラーを使用します。例えば更新パネルをクリアする前に、ユーザーにデータを保存するようプロンプトを出すようにするなどです。
oncleared イベント・ハンドラーは、その他すべてのフォーム・フィールドをクリアしたり、フォームをリセットしたりするために使用します。更新パネルをレポート・ソリューションで使用している場合、フォームには Clear ボタンを配置することができます。このボタンはパネル非表示関数を呼び出し、パネルをクリアしたりする時点でレポート基準をクリアすることができます。
以下の 2 つの重要な関数は、<ajax:page/> によって呼び出され、<ajax:updatepanel/> コントロールに対して実行されます。
showUpdatePanel– 対応するonShowイベント・ハンドラーを呼び出すことによって、更新パネルを表示するhideUpdatePanel– 対応するonHideイベント・ハンドラーを呼び出すことによって、更新パネルを非表示にする
この 2 つの関数はほとんど同じで、ダイアログ固有のインスタンスに対する show ダイアログ関数と hide ダイアログ関数を囲むラッパーでしかありません。関数には、controlName 引数によってダイアログの名前が渡されます。すると、ダイアログの名前と _onShow を連結して、onShow 関数の名前が動的に作成されます。名前が作成されると、window['functionName']() という JavaScript 文によって <ajax:updatepanel/> タグ内でレンダリングされるパネル固有の関数が呼び出されます。リスト 1 に、showUpdatePanel 関数を記載します。
リスト 1.
showUpdatePanel JavaScript 関数
function showUpdatePanel(controlName) {
var functionName = controlName + '_onShow';
window[functionName]();
}
|
この手法を使うことにより、ダイアログを表示する方法、そしてコンテンツを非同期に取得する方法についての詳細が、<ajax:updatepanel/> コントロールを使用している Web アプリケーション開発者に対して抽象化されます。そのため開発者は、showUpdatePanel または hideUpdatePanel JavaScript 関数を呼び出して、表示あるいは非表示にするパネルの名前を渡せばよいだけとなります。
次のステップは、showUpdatePanel JavaScript 関数が呼び出すパネル固有の関数を作成することです。この関数は <ajax:updatepanel/> タグから呼び出され、以下の役割を果たします。
onclear、oncleared、onload、およびonloadedタグの属性に指定されたイベント・ハンドラーを呼び出す- 更新パネルのコンテンツを取得するための非同期リクエストを行う
cssclass、loadingcss、updatemessageなどの属性に指定されたオプションを使用して更新パネルを表示する
まずは、dialogName_onShow 関数から取り掛かります (リスト 2 を参照)。
リスト 2.
dialogName_onShow JavaScript 関数
function cityPanel_onShow() {
// Invoke the onload event.
// If true is not return by the event handler,
// cancel the update to the panel
if (!window['onCityPanelLoad']()) {
return;
}
// Get the dialog element
var controlName = 'cityPanel_Container';
var curControl= document.getElementById(controlName);
// Update status message to the value specified in the
// updatemessage attribute
if (curControl != null) {
curControl.innerHTML = 'Retrieving cities...';
curControl.className = 'loadingpanel';
}
// Url for retrieving dialog content (specified in url attribute)
var targetUrl = '/ajaxcontrols3/fragments/cityList.jsp';
// Build the POST data sent for the asynchronous request to
// retrieve the update panel content
// The arguments specified in the contained panelargument tags
// are dynamically added to the post data
var postData = 'panelId=cityPanel' + '&state=' + getFormValue('state')
+ '&cityPrefix=' + getFormValue('cityPrefix');
// Initialize XmlHttpRequest object
initializeXmlHttpRequest();
if (req!=null) {
req.onreadystatechange=cityPanel_onServerResponse;
// Set window status
window.status='Retrieving cities...';
// Open server request
req.open('POST',targetUrl,true);
req.setRequestHeader("Content-type",
"application/x-www-form-urlencoded");
req.setRequestHeader("Content-length",
postData.length);
// Send data
req.send(postData);
}
}
|
オプションのイベント・ハンドラー、onclear、oncleared、onload、および onloaded は、いずれも同じ window['functionName']() JavaScript 文を使用して呼び出されます。
非同期リクエストに対して送信されるデータは、動的にリクエストに追加されます。使用するのは、GET ではなく POST です。これは、クエリー・ストリングが長すぎる、あるいはストリングをエスケープするという問題を避けるためです。関数に含まれる panelargument タグごとに、名前と値のペアが POST データに追加されます。この値は、value 属性を使って静的にすることも、sourcefield 属性を使って動的にすることもできます。
更新パネルを表示する際の最後のステップは、サーバー・レスポンスを処理することです。これは、dialogName_onServerResponse 関数によって行います。このコードは複雑ではありません。レスポンスをチェックして、エラーがなければ、更新パネルに含まれるコンテンツをサーバー・レスポンスに置き換えるだけのことです。リスト 3 に、この関数を示します。
リスト 3.
dialogName_onServerResponse JavaScript 関数
function cityPanel_onServerResponse() {
// If loaded
if(req.readyState!=4) return;
// If an error occurred
if(req.status != 200) {
alert('An error occurred retrieving data.');
return;
}
// Get panel
var curControl= document.getElementById('cityPanel_Container');
// If panel not found simply return (this shouldn't happen)
if (curControl == null)
return;
// Update CSS class to the value specified in the cssclass attribute
curControl.className = 'normalpanel';
// Set content of element to the server response
curControl.innerHTML = req.responseText;
// Clear window status
window.status='';
// Invoke the onloaded event handler (this is not rendered
// if there is no event handler specified in the oncleared
// attribute)
window['onCityPanelLoaded']();
}
|
今度は dialogName_onHide 関数を作成します。この関数は、hideUpdatePanel 関数から呼び出されます。パネルをクリアするという明らかな目的の他、この関数にはカスタム・イベント・ハンドラーにビジネス・ルールを適用させるという重要な目的があります。この関数はリスト 4 のとおりです。
リスト 4.
dialogName_onHide JavaScript 関数
function onHide_cityPanel() {
// Invoke the onclear event.
// If true is not returned by the event handler,
// the action is cancelled and the panel remains
// visible
if(!window['onCityPanelClear']()) {
return;
}
// Retrieve the update panel container
var controlName = 'cityPanel_Container';
var curControl= document.getElementById(controlName);
// If control wasn't found, return
if (curControl == null) {
alert(controlName + ' control not found!');
return;
}
// Clear tag
curControl.innerHTML = '';
// Invoke the oncleared event handler (this is not rendered
// if there is no event handler specified in the oncleared
// attribute)
window['onCityPanelCleared']();
}
|
次のステップでは、TagLib ライブラリー定義ファイルに <ajax:updatepanel/> JSP コントロールを定義します。リスト 5 に、更新パネル・コントロールの TagLib ライブラリー定義エントリーを記載します (各属性の説明としてコメントを組み込んであります)。
リスト 5. 更新パネル TagLib ライブラリー定義エントリー
<tag> <name>updatepanel</name> <tagclass>com.testwebsite.controls.updatepanel.UpdatePanelTag</tagclass> <bodycontent>JSP</bodycontent> <info>Update Panel tag.</info> <!-- Panel ID--> <attribute> <name>id</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Url to fragment--> <attribute> <name>url</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Message displayed while retrieving panel content--> <attribute> <name>updatemessage</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Loading CSS class name --> <attribute> <name>loadingcss</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- CSS class name (Post loading) --> <attribute> <name>cssclass</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Event: on clear --> <attribute> <name>onclear</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Event: on cleared --> <attribute> <name>oncleared</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Event: on loading--> <attribute> <name>onload</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Event: post loaded--> <attribute> <name>onloaded</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag> |
リスト 6 に、TagLib ライブラリー定義ファイル内の <ajax:panelargument/> JSP コントロールを記載します (各属性の説明としてコメントを組み込んであります)。
リスト 6. パネル引数 TagLib ライブラリー定義エントリー
<tag> <name>panelargument</name> <tagclass>com.testwebsite.controls.updatepanel.PanelArgumentTag</tagclass> <bodycontent>empty</bodycontent> <info>Contains the argument tag.</info> <!-- Argument name --> <attribute> <name>name</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Source field for argument value --> <attribute> <name>sourcefield</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Value for argument (overrides source field value if specified --> <attribute> <name>value</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag> |
ポップアップ・ダイアログ JSP TagLib コントロールの作成
Web アプリケーションでポップアップ・ウィンドウを表示するために従来から Web 開発者が使用している関数は、window.open または window.showModalDialog です。それでもまったく問題なく機能しますが、これらの関数を使うと厄介で複雑にならざるを得ません。第一に、他のフォーム・フィールドを参照するのが面倒です。第二に、表示は DOM (Document Object Model) スクリプティングと CSS ベースのダイアログ・ボックスほど動作が軽くなく、直観的でもありません。第三に、例えばユーザーがページ上の項目にマウス・カーソルを重ねたときなどに追加情報を表示する場合、円滑に表示されるようにはなりません。
一方、DOM スクリプティングと CSSの z-index プロパティーを使用すれば、Web ページのダイアログを模倣することができます。Ajax および非同期の手法により、必要に応じてサーバーからデータを非同期に取得できるため、実に強力で柔軟なダイアログ・ボックスを作成することができます。
<ajax:popupdialog/> での主な目的は、DOM スクリプティング、CSS、Ajax を組み合わせた JSP ベースのポップアップ・ダイアログ・コントロールを作成し、どのビジネス分野のアプリケーションにでも簡単に追加することができる再利用可能なコントロールを作成することです。表 2 に、このポップアップ・ダイアログ・コントロールを構成するタグを記載します。
表 2. ポップアップ・ダイアログ・コントロールのタグ
| タグ | 内容 |
|---|---|
| popupdialog | ポップアップ・ダイアログ |
| popuparguments | ポップアップ・ダイアログに渡す 1 つ以上の引数 (popupargument タグ) が含まれるオプションのタグ |
| popupargument | ポップアップ・ダイアログのポップアップ・ダイアログ引数 |
| popupbuttoncontainer | 1 つ以上のボタン (popupbutton タグ) が含まれるオプションのタグ。ボタン・レイアウトをフォーマット設定するための HTML を含めることもできます。 |
| popupbutton | ポップアップ・ダイアログのボタン |
図 8 に、ポップアップ・ダイアログのコンポーネントを示します。
図 8. サンプル・ポップアップ・ダイアログ・コンテナー
ページが初めてレンダリングされるときには、ダイアログ・ボックスの DIV コンテナーがレンダリングされます。このときに適用されるスタイルは、ポップアップ・ダイアログを非表示にする display: none です。コンテナーにはオプションで、タイトル・パネル (showtitle 属性と title 属性に依存) とボタン・パネル (<ajax:popupdialog/> に <ajax:popupbuttoncontainer/> タグが含まれているかどうかに依存) が含まれることもあります。また、ダイアログ・コンテナー内ではダイアログのコンテンツ用 DIV もレンダリングされます。この要素は初期の段階では空で、ダイアログのコンテンツが追加されるのは、対応するポップアップ・ダイアログに対して showDialog 関数が呼び出された時点です。ポップアップ・ダイアログが表示されるときには、以下のことが起こります。
- スタイルが
display: blockに変更され、ポップアップ・ダイアログが可視になります。 - ダイアログのコンテンツが取得されている間、ポップアップ・ダイアログに
updatemessageが表示されます。 - サーバーに対して、ダイアログのコンテンツを取得するための非同期リクエストが行われます。
- サーバー・レスポンスを受信した時点で、ダイアログのコンテンツが表示されます。
ポップアップ・ダイアログ・コンテナーを機能させるために、2 つの重要な CSS プロパティーも適用されます。最初の CSS プロパティーは z-index: 100; です。この値によって、他の Web ページ要素の上にダイアログ・コンテナーが表示されることが確実になります。2 番目に適用されるプロパティーは position: absolute; で、この値がポップアップ・ダイアログの位置指定を可能にします。
ポップアップ・ダイアログ・コントロールには、ポップアップ・ダイアログの振る舞いとプレゼンテーションを定義するために構成できる機能がいくつか用意されています。サポートされる構成可能な機能は、表 3 に記載するとおりです。
表 3. ポップアップ・ダイアログ・コントロールの構成可能な機能
| 機能 | 内容 |
|---|---|
| id | コントロールの ID |
| title | ダイアログのタイトル |
| showtitle | ダイアログのタイトルを表示するかどうかを制御するオプション (デフォルト: true) |
| showclose | 閉じるボタンを表示するかどうかを制御するオプション (デフォルト: true) |
| width | ダイアログの全幅 |
| height | ダイアログの全高 |
| contentheight | ダイアログ内のスクロール可能コンテンツ・ペインの高さ |
| left | ダイアログ左端の位置 |
| top | ダイアログ上端の位置 |
| url | ダイアログを表示する際に、そのコンテンツを取得するための URL |
| updatemessage | コンテンツの取得中に表示するメッセージ |
ポップアップ・ダイアログを表示/非表示にするための関数を作成する
以下の 2 つの重要な関数は、<ajax:page/> によって呼び出され、<ajax:popupdialog/> コントロールに対して実行されます。
showDialog– 対応するonShowイベント・ハンドラーを呼び出すことによって、ポップアップ・ダイアログを表示するcloseDialog– 対応するonHideイベント・ハンドラーを呼び出すことによって、ポップアップ・ダイアログを非表示にする
更新パネルと同様、この 2 つの関数はほとんど同じで、ダイアログ固有のインスタンスに対する show ダイアログ関数と hide ダイアログ関数を囲むラッパーでしかありません。更新パネルで説明したように、この手法は <ajax:popupdialog/> コントロールを使用しているユーザーに対して、ダイアログがどのように表示されて、コンテンツがどのように非同期で取得されるかについての詳細を抽象化します。そのため、必要な作業は showDialog または closeDialog JavaScript 関数を呼び出して、表示あるいは非表示にするダイアログの名前を渡すことだけです。
次のステップとして、ポップアップ・ダイアログを表示するためのダイアログ固有の関数を作成します。ほとんどの場合 <ajax:popupdialog/> タグによって呼び出され、レンダリングされるこの関数には、タグの属性に指定されたオプションに基づく JavaScript コードが含まれます。例えば、ダイアログのタイトルを設定し、POST データと URL を組み立てて非同期リクエストを行うなどのコードです。リスト 7 にダイアログを表示するための関数を記載します (訳注: 以下、関数名の dialogName の部分は実際のダイアログ名 (リスト 7 の場合 stateDialog) に置き換わります)。
リスト 7.
dialogName_onShow JavaScript 関数
function stateDialog_onShow() {
// Get dialog control
var dialogControl = document.getElementById('stateDialog');
dialogControl.style.display = 'block';
// Set title (if showtitle attribute is not false)
var titleControl = document.getElementById('stateDialog_dialogTitleText');
titleControl.innerHTML = 'Select States';
// Get dialog content control
var dialogContentName = 'stateDialog_dialogContent';
var contentControl = document.stateDialog_dialogForm[dialogContentName];
// If dialog content control is not null
if (contentControl != null) {
contentControl.innerHTML = 'null';
}
// Initialize XmlHttpRequest object
initializeXmlHttpRequest();
if (req == null) {
alert('XmlHttpRequest object not initialized.');
}
// Url for retrieving dialog content (specified in url attribute)
var targetUrl = '/ajaxcontrols3/fragments/selectState.jsp';
// Build the POST data sent for the asynchronous request to
// retrieve the dialog content
// The arguments specified in the optional argument tag(s)
// contained in the popuparguments tag
var postData = '';
// The dialogName_getPostData function is rendered by the
// popuparguments tag (if specified).
// Check if the function is defined and if so, invoke the
// function
if (window['stateDialog_getPostData'] != undefined) {
postData = window['stateDialog_getPostData']();
}
// Set server response function
var responseFunction = 'stateDialog_onServerResponse';
req.onreadystatechange = eval(responseFunction);
// Open server request
req.open('POST',targetUrl,true);
req.setRequestHeader("Content-type",
"application/x-www-form-urlencoded");
req.setRequestHeader("Content-length",
postData.length);
// Send data
req.send(postData);
}
|
次のステップは、サーバー・レスポンスを処理することです。これは、dialogName_onServerResponse 関数によって行います。この関数のコードは難しいものではありません。ダイアログのコンテンツを非同期サーバー・リクエストによって返された HTML に置き換えるだけです。リスト 8 に、サーバー・レスポンス関数を記載します。
リスト 8.
dialogName_onServerResponse JavaScript 関数
function stateDialog_onServerResponse() {
// If loaded
if(req.readyState!=4)
return;
// If an error occurred
if(req.status != 200) {
alert('An error occurred retrieving panel content.');
return;
}
// Get response
var responseData = req.responseText;
// Get dialog content pane
var dialogContentName = 'stateDialog_dialogContent';
var contentControl = document.getElementById(dialogContentName);
// If dialog content pane found, set the content
if (contentControl != null) {
contentControl.innerHTML = responseData;
}
}
|
ポップアップ・ダイアログ TagLib ライブラリー定義エントリー
次のステップでは、TagLib ライブラリー定義ファイルに <ajax:popupdialog/> JSP コントロールを定義します。リスト 9 に、ポップアップ・ダイアログ・コントロールの TagLib ライブラリー定義エントリーを記載します (各属性の説明としてコメントを組み込んであります)。
リスト 9. ポップアップ・ダイアログ TagLib ライブラリー定義エントリー
<tag> <name>popupdialog</name> <tagclass>com.testwebsite.controls.popupdialog.PopupDialogTag</tagclass> <bodycontent>JSP</bodycontent> <info>Popup Dialog tag.</info> <!-- Popup ID--> <attribute> <name>id</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Popup Title--> <attribute> <name>title</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Show Title --> <attribute> <name>showtitle</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Show Close Button --> <attribute> <name>showclose</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Size Width --> <attribute> <name>width</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Size Height --> <attribute> <name>height</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Size Content Height --> <attribute> <name>contentheight</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Position Left --> <attribute> <name>left</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Position Top--> <attribute> <name>top</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Url to fragment--> <attribute> <name>url</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Message displayed while retrieving panel content--> <attribute> <name>updatemessage</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag> |
ポップアップ引数コンテナーおよびポップアップ引数コントロール
<ajax:popupdialog/> には、ダイアログのコンテンツを取得するための非同期リクエストとして、POST データを送信するための <ajax:popuparguments/> タグをオプションで含めることができます。このタグの役目は、dialogName_getPostData 関数を呼び出すことです。リスト 10 に一例として、単純に POST データの名前と値のペアがすべて含まれるストリングを返す dialogName_getPostData 関数を記載します。
リスト 10.
dialogName_getPostData JavaScript 関数
function cityDetailsDialog_getPostData() {
var data = 'dialogName=cityDetailsDialog'
+ '&zipCode=' + getFormValue('selectedZipCode');
return data;
}
|
名前と値のペアは、popupargument タグごとに提供されます。各引数には、+ '&zipCode=' + getFormValue('selectedZipCode'); というコード・フラグメントが提供されます。popupargument タグについては、後で詳しく説明します。
doStartTag は、dialogName_getPostData 関数の開始部分を記述します。<ajax:popuparguments/> の場合、このメソッドはリスト 11 のようになります。
リスト 11. ポップアップ引数コンテナーの
doStartTag メソッド
public int doStartTag() throws JspException {
StringBuffer html = new StringBuffer();
String dialogName = this.getDialogName();
html.append("<script type='text/javascript' language='javascript'>");
html.append("function ");
html.append(dialogName);
html.append("_getPostData() {");
html.append("var data = 'dialogName=");
html.append(dialogName);
html.append("'");
JspWriter out = pageContext.getOut();
try {
out.append(html.toString());
} catch (IOException e) {
e.printStackTrace();
}
return PopupDialogArgumentContainerTag.EVAL_BODY_INCLUDE;
}
|
doEndTag メソッドは dialogName_getPostData 関数の終了部分を記述します (リスト 12 を参照)。
リスト 12. ポップアップ引数コンテナーの
doEndTag メソッド
public int doEndTag() throws JspException {
StringBuffer html = new StringBuffer();
html.append(";");
html.append("return data;");
html.append("}");
html.append("</script>");
// Write output
JspWriter out = pageContext.getOut();
try {
out.append(html.toString());
} catch (IOException e) {
e.printStackTrace();
}
return PopupDialogArgumentContainerTag.EVAL_PAGE;
}
|
続いて、TagLib ライブラリー定義ファイルに <ajax:popuparguments/> JSP コントロールを定義します。リスト 13 に、ポップアップ・ダイアログ引数コンテナーの TagLib ライブラリー定義エントリーを記載します (各属性の説明としてコメントを組み込んであります)。
リスト 13. ポップアップ・ダイアログ引数コンテナーの TagLib ライブラリー定義エントリー
<tag> <name>popuparguments</name> <tagclass>com.testwebsite.controls.popupdialog.PopupDialogArgumentContainerTag </tagclass> <bodycontent>JSP</bodycontent> <info>Popup Dialog Argument Container</info> <!-- Popup Argument Container ID--> <attribute> <name>id</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag> |
次のステップでは、TagLib ライブラリー定義ファイルに <ajax:popupargument/> JSP コントロールを定義します。ポップアップ引数の TagLib ライブラリー定義エントリーは、リスト 14 のとおりです (各属性の説明としてコメントを組み込んであります)。
リスト 14. ポップアップ引数 TagLib ライブラリー定義エントリー
<tag> <name>popupargument</name> <tagclass>com.testwebsite.controls.popupdialog.PopupArgumentTag</tagclass> <bodycontent>empty</bodycontent> <info>Contains the argument tag.</info> <!-- Argument name --> <attribute> <name>name</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Source field for argument value --> <attribute> <name>sourcefield</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Value for argument (overrides source field value if specified) --> <attribute> <name>value</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag> |
<ajax:popupdialog/> には、ボタン・パネルの <ajax:popupbuttoncontainer/> タグをオプションで含めることができます。このコンテナー・タグがレンダリングするのは、dialogName_dialogButtonContainer という名前の DIV 要素です。
<ajax:popupbuttoncontainer/> タグには、1 つ以上の <ajax:popupbutton/> タグを含められます。ポップアップ・ダイアログ・コントロールでサポートされるボタンのタイプは、cancel と normal の 2 つです。ボタンのタイプが cancel の場合、ボタンがクリックされると closeDialog が呼び出されます。ボタンのタイプが normal であれば、onclick 属性に指定された JavaScript 関数が呼び出されます。
ボタンは、ポップアップ・ボタンの doStartTag メソッド内でレンダリングされます。このメソッドには、ボタン・タイプのロジックと呼び出されるイベント・ハンドラーが含まれます (リスト 15 を参照)。
リスト 15. ポップアップ・ボタンの
doStartTag メソッド
public int doStartTag() throws JspException {
StringBuffer html = new StringBuffer();
// Get button container
PopupDialogButtonContainerTag buttonPane =
(PopupDialogButtonContainerTag) this.getParent();
String dialogName = buttonPane.getDialogName();
// Render button
html.append("<input type='button' id='");
html.append(this.getId());
html.append("' class='");
html.append(this.getCssclass());
String buttonLabel = this.getLabel();
if (buttonLabel != null && buttonLabel.length() > 0) {
html.append("' value='");
html.append(buttonLabel);
}
html.append("' onclick='");
// Check button type and render event handler accordingly
String buttonType = this.getType();
if (buttonType == null ||
buttonType.length() < 1 ||
buttonType.equalsIgnoreCase("cancel")) {
html.append("closeDialog(\"");
html.append(dialogName);
html.append("\")");
}
else {
html.append(this.getOnclick());
html.append("(\"");
html.append(dialogName);
html.append("\", document.");
html.append(dialogName);
html.append("_dialogForm)");
}
html.append("'/>");
// Write output
JspWriter out = pageContext.getOut();
try {
out.append(html.toString());
} catch (IOException e) {
e.printStackTrace();
}
return PopupButtonTag.EVAL_BODY_BUFFERED;
}
|
次のステップでは、TagLib ライブラリー定義ファイルに <ajax:popupbuttoncontainer/> JSP コントロールを定義します。ボタン・パネル・コンテナーの TagLib ライブラリー定義エントリーは、リスト 16 のとおりです (各属性の説明としてコメントを組み込んであります)。
リスト 16. ポップアップ・ダイアログのボタン・コンテナー TagLib ライブラリー定義エントリー
<tag> <name>popupbuttoncontainer</name> <tagclass>com.testwebsite.controls.popupdialog.PopupDialogButtonContainerTag </tagclass> <bodycontent>JSP</bodycontent> <info>Popup Dialog Button Container</info> <!-- Popup Button Container ID--> <attribute> <name>id</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag> |
次に、TagLib ライブラリー定義ファイルに <ajax:popupbutton/> JSP コントロールを定義します。リスト 17 に、ボタン TagLib ライブラリー定義エントリーを記載します (各属性の説明としてコメントを組み込んであります)。
リスト 17. ポップアップ・ボタン TagLib ライブラリー定義エントリー
<tag> <name>popupbutton</name> <tagclass>com.testwebsite.controls.popupdialog.PopupButtonTag</tagclass> <bodycontent>empty</bodycontent> <info>Contains the argument tag.</info> <!-- Button id --> <attribute> <name>id</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <!-- Button label --> <attribute> <name>label</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> <!-- Type of button --> <attribute> <name>type</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- onclick Event handler --> <attribute> <name>onclick</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- CSS class name --> <attribute> <name>cssclass</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag> |
新しく作成した Ajax 対応コントロールを実演するために、3 つのテスト・ページが用意されています。表 4 に、これらのページについて説明します。
表 4. テスト・ページ
| ページ | 内容 |
|---|---|
| SearchLocations.jsp | <ajax:updatepanel/> を使用して、都市が含まれるデータ・グリッドをリアルタイムでフィルタリングします。都市一覧のフィルタリング基準として使用できるのは、州 (ドロップダウン選択リスト) および都市名 (テキスト・フィールド) です。この 2 つのフィールドは、それぞれの値が変更されると showUpdatePanel を呼び出します。 |
| LocationReport.jsp | <ajax:popupdialog/> を使用して模擬レポート・ページを表示します。レポート基準には州のテキスト・フィールドが組み込まれていて、このフィールドをクリックすると、すべての州がチェック・ボックス付きの一覧として含まれたポップアップ・ダイアログが表示されます。ユーザーが Save ボタンをクリックすると、州の一覧がカンマ区切りリストとして作成され、州のテキスト・フィールドの値がこのカンマ区切りリストに設定されます。 |
| NewYorkCityList.jsp | ニューヨーク州のすべての都市の一覧を表示します。ユーザーが都市一覧の中に記載されている郵便番号にマウス・カーソルを重ねると、このページは <ajax:popupdialog/> を使用して該当する都市の追加情報を表示します。郵便番号からフォーカスが離れると、ポップアップ・ダイアログが非表示になります。 |
ロケーション検索ページ (SearchLocations.jsp)
ロケーション検索ページが実演するのは、更新パネル・コントロールの使用方法です。図 9 に、サンプル更新パネルを示します。
図 9. 更新パネルのデモ: ロケーション検索ページ
ロケーション検索ページはイベント・ハンドラーの使用方法、そして更新パネルを 2 つのコントロール (SELECT および INPUT テキスト・ボックス) に接続する方法を実際に示します。リスト 18 に、このサンプル・ページのコードを記載します。
リスト 18. SearchLocations.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="ajax" uri="/WEB-INF/tlds/ajax_controls.tld"%>
<html>
<head>
<title>Locations</title>
<link href="core.css" rel="stylesheet" type="text/css" />
<ajax:page/>
<script type='text/javascript' language='javascript'>
function onCityPanelCleared() {
var msgControl = document.getElementById('statusMessage');
if (msgControl != null) {
msgControl.innerHTML = 'oncleared';
}
}
function onCityPanelClear() {
var msgControl= document.getElementById('statusMessage');
if (msgControl != null) {
msgControl.innerHTML = 'onclear';
}
return true;
}
function onCityPanelLoad() {
var msgControl = document.getElementById('statusMessage');
if (msgControl != null) {
msgControl.innerHTML = 'onload';
}
return true;
}
function onCityPanelLoaded() {
var msgControl= document.getElementById('statusMessage');
if (msgControl != null) {
msgControl.innerHTML = 'onloaded';
}
}
function displayCityPanel(curControl) {
if (!curControl.checked) {
hideUpdatePanel('cityPanel');
}
}
function retrieveCityData() {
showUpdatePanel('cityPanel');
document.all.showCityPanel.checked = 'checked';
}
</script>
</head>
<body>
<b>State: </b>
<select id="state" onchange="retrieveCityData()">
<option value=''> </option>
<option value='AK'> Alaska</option>
<option value='AL'> Alabama</option>
<option value='AR'> Arkansas</option>
...
...
<option value='WI'> Wisconsin</option>
<option value='WV'> West Virginia</option>
<option value='WY'> Wyoming</option>
</select><br/>
<b>City Prefix: </b>
<input type="text" id="cityPrefix" onkeyup="retrieveCityData()"/>
<br/>
<input type="checkbox"
id="showCityPanel"
name="showCityPanel"
checked="checked"
onclick="displayCityPanel(this)" />Show City Panel<br/>
<div><b>Result: </b><span id="statusMessage"></span></div>
<div>
<ajax:updatepanel id="cityPanel"
url="/fragments/cityList.jsp"
updatemessage="Retrieving cities..."
cssclass="normalpanel"
loadingcss="loadingpanel"
onclear="onCityPanelClear"
oncleared="onCityPanelCleared"
onloaded="onCityPanelLoaded"
onload="onCityPanelLoad">
<ajax:panelargument name="state" sourcefield="state"/>
<ajax:panelargument name="cityPrefix" sourcefield="cityPrefix"/>
</ajax:updatepanel>
</div>
</body>
</html>
|
ロケーション・レポート (LocationReport.jsp)
ロケーション・レポートは、ポップアップ・ダイアログを使ってユーザーが複数の州を (チェック・ボックスで) 選択できるようにする方法を実演するページです。ユーザーがポップアップ・ダイアログの Save ボタンを選択すると、選択された州がカンマ区切りリストとしてテキスト・ボックスに設定されます。図 10 に、このポップアップ・ダイアログを示します。
図 10. ポップアップ・ダイアログのデモ: ロケーション・レポート
ユーザーが selectedStates コントロールをダブルクリックすると、showDialog 関数によってポップアップ・ダイアログが表示されます。popupdialog でユーザーが Save ボタンをクリックすると、onSaveStates JavaScript 関数が呼び出され、選択された値のカンマ区切りリストが作成され、続いて selectedStates テキスト・ボックスにそのリストが設定されます。ロケーション・レポートのコードはリスト 19 のとおりです。
リスト 19. LocationReport.jsp
<%@ page language="java" contentType="text/html;
charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="ajax"
uri="/WEB-INF/tlds/ajax_controls.tld"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=ISO-8859-1">
<title>Search Locations</title>
<link href="core.css" rel="stylesheet"
type="text/css" />
<script type="text/javascript">
function onSaveStates(dialogName, dialogForm) {
var values = '';
var stateList = dialogForm.states;
for (var i = 0; i < stateList.length; i++) {
if (stateList[i].checked) {
if (values.length != 0) {
values += ", ";
}
values += stateList[i].value;
}
}
var targetControl = document.getElementById('selectedStates');
targetControl.value = values;
closeDialog(dialogName);
}
</script>
<ajax:page id="page"/>
</head>
<body>
<h1>Report Criteria</h1>
<table width="80%" >
<colgroup>
<col width="40%"/>
<col width="60%"/>
</colgroup>
<tbody>
<tr>
<td align="right" style="border:none;">
<span style="font-weight:bold">Zip Code:
</span>
</td>
<td align="left" style="border:none;">
<input type="text"
size="50"
id="zipCode" />
</td> </tr>
<tr>
<td align="right" style="border:none;">
<span style="font-weight:bold">States:
</span>
</td>
<td align="left" style="border:none;">
<input type="text"
size="50"
readonly="readonly"
id="selectedStates"
onclick="showDialog('stateDialog')"/>
</td> </tr>
</tbody>
<thead>
<th colspan="2" class="subHeading">
Search Locations</th>
</thead>
<tfoot>
<tr>
<td colspan="2">
<input type="button" class="dialogButton"
value="Run Report" id="runReport"/>
</td> </tr>
</tfoot>
</table>
<ajax:popupdialog url="/ajaxcontrols3/fragments/selectState.jsp"
id="stateDialog"
width="400px" height="28em"
left="0" top="0"
title="Select States"
contentheight="23em">
<ajax:popuparguments id="stateDialogArgs">
<ajax:popupargument name="arg1" value="test1"/>
<ajax:popupargument name="arg2" value="test2"/>
</ajax:popuparguments>
<ajax:popupbuttoncontainer id="stateDialogButtons">
<ajax:popupbutton id="cancelButton"
label="Cancel" type="cancel"/>
<ajax:popupbutton id="saveButton" label="Save"
type="normal" onclick="onSaveStates"/>
</ajax:popupbuttoncontainer>
</ajax:popupdialog>
<h1>Report Results</h1>
<div>Show report results here...</div>
</body>
</html>
|
リスト 20 に、ポップアップ・ダイアログ・ボックス内に表示されるページを記載します。このコードは比較的単純なもので、Location Data Service からロケーションの一覧を取得し、それからこの一覧を繰り返し処理して各州のチェック・ボックスをレンダリングするという内容です。
リスト 20. selectState.jsp
<%@ page language="java" contentType="text/html;
charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ page import="com.testwebsite.dal.LocationDataService" %>
<%@ page import="com.testwebsite.dto.LocationDTO" %>
<%@ page import="com.testwebsite.dto.StateDTO" %>
<%@ page import="java.util.Iterator" %>
<%@ page import="java.util.TreeMap" %>
<%@ page import="java.util.Set" %>
<ul style="list-style: none;"
<%
TreeMap<String, StateDTO> stateData =
LocationDataService.getStateData();
// Iterate through all data looking for matching
// cities and add to temporary TreeMap
Set<String> keySet = stateData.keySet();
Iterator<String> stateIter = keySet.iterator();
while (stateIter.hasNext()) {
// Get current state
String curKey = stateIter.next();
StateDTO curState = stateData.get(curKey);
out.println("<li><input type='checkbox'
name='states' value='");
out.println(curState.getAbbreviation());
out.println("'/> ");
out.println(curState.getName());
out.println("</li>");
}
%>
</ul>
|
ニューヨーク州の都市一覧ページ (NewYorkCityList.jsp)
ニューヨーク州の都市一覧ページは、ポップアップ・ダイアログ・ボックスを利用する別の方法を実演します。多くの Web アプリケーションで共通のシナリオは、ユーザーが一覧内の項目にマウス・カーソルを重ねると、追加情報が表示されるというものです。このポップアップ・ダイアログ・ボックスでは、Ajax で要求された場合にのみ詳細情報を取得することができます。図 11 に、このサンプル・ポップアップ・ダイアログ・ボックスを示します。
図 11. サンプル・ポップアップ・ダイアログ・コンテナー
ニューヨーク州の都市一覧ページのコードをリスト 21 に記載します。このサンプルで重要な関数は showCityDialog です。この関数はユーザーが現在マウス・カーソルを重ねている項目の郵便番号だけをパラメーターとして取ります。ダイアログ・ボックスの位置は、標準 CSS プロパティーと JavaScript コードによって設定されます。位置が設定された後に続くのは、showDialog 関数です。ユーザーが郵便番号からマウス・カーソルのフォーカスを離すと、hideDialog 関数が呼び出されてポップアップ・ダイアログが非表示になります。
リスト 21. NewYorkCityList.jsp
<html><head>
<title>New York City Listing</title>
<ajax:page/>
<link href="core.css" rel="stylesheet"
type="text/css" />
</head>
<script type="text/javascript">
function showCityDialog(zipCode) {
var xPosition = 0;
var yPosition = 0;
if (!e) var e = window.event;
if (e.pageX || e.pageY) {
posx = e.pageX;
yPosition = e.pageY;
}
else if (e.clientX || e.clientY) {
xPosition = e.clientX + document.body.scrollLeft
+ document.documentElement.scrollLeft;
yPosition = e.clientY + document.body.scrollTop
+ document.documentElement.scrollTop;
}
var dialogControl = document.getElementById("cityDetailsDialog");
if (dialogControl != null) {
dialogControl.style.top = yPosition;
dialogControl.style.left = xPosition;
}
// Get selected zip code
var zipCodeControl = document.getElementById("selectedZipCode");
if (zipCodeControl != null) {
zipCodeControl.value = zipCode;
showDialog('cityDetailsDialog');
}
}
</script>
<body>
<table cellpadding="0" cellspacing="0" width="60%">
<tbody> <%
TreeMap<Integer, LocationDTO> locData =
LocationDataService.getLocationData();
// Temp map to ensure only unique cities are added.
HashMap tempMap = new HashMap();
// Iterate through all data looking for matching cities and add to
// temporary TreeMap
Set<Integer> keySet = locData.keySet();
Iterator<Integer> locIter = keySet.iterator();
while (locIter.hasNext()) {
// Get current state
Integer curKey = locIter.next();
LocationDTO curLocation = locData.get(curKey);
String curState = curLocation.getState();
String curCity = curLocation.getCity();
String lowerCurCity = curCity.toLowerCase();
if (!tempMap.containsKey(curCity) &&
curState.equalsIgnoreCase("NY")) {
tempMap.put(curCity, curCity);
out.println("<tr>");
out.println("<td><a href='#'
style='text-decoration:none;color'
onMouseOver=\"showCityDialog('" +
curLocation.getZipCode() +
"')\" >");
out.println(curLocation.getZipCode());
out.println("</td>");
out.println("<td>");
out.println(curLocation.getCity());
out.println("</td>");
out.println("<td>");
out.println(curLocation.getCounty());
out.println("</td>");
out.println("</tr>");
}
} %>
</tbody>
<thead> <tr>
<th colspan="3"><b>Cities</b></th></tr>
<tr>
<th class="subHeading"> <b>Zip Code</b></th>
<th class="subHeading">
<b>City</b></th>
<th class="subHeading">
<b>County</b></th>
</tr> </thead>
</table>
<input type="hidden" id="selectedZipCode"
value=""/>
<ajax:popupdialog url="/ajaxcontrols3/fragments/cityDetails.jsp"
id="cityDetailsDialog" width="400px"
left="0" top="0" title="City Information"
height="10.5em" contentheight="8.5em">
<ajax:popuparguments id="stateDialogArgs">
<ajax:popupargument name="zipCode"
sourcefield="selectedZipCode"/>
</ajax:popuparguments>
</ajax:popupdialog>
</body></html>
|
リスト 22 に、ポップアップ・ダイアログのコンテンツに対応する JSP コードを記載します。このコードは見てのとおり、都市の詳細情報を Location Data Service から取得して、対応するレコードを表示します。
リスト 22. cityDetails.jsp
<%@ page language="java" contentType="text/html;
charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ page import="com.testwebsite.dal.LocationDataService" %>
<%@ page import="com.testwebsite.dto.LocationDTO" %>
<%@ page import="com.testwebsite.dto.StateDTO" %>
<%@ page import="java.util.Iterator" %>
<%@ page import="java.util.TreeMap" %>
<%@ page import="java.util.Set" %>
<%
TreeMap<Integer, LocationDTO> locData = LocationDataService.getLocationData();
String zipCodeStr = request.getParameter("zipCode");
Integer zipCode = new Integer(zipCodeStr);
LocationDTO curLocation = locData.get(zipCode);
if (curLocation != null) {
out.println("<div id='cityDetails'>");
// Render zip code
out.println("<div>");
out.println("<span style='font-weight:bold;color:
#00628B'>Zip Code: </span>");
out.println("<span style='color: #00628B'>");
out.println(curLocation.getZipCode());
out.println("</span>");
out.println("</div>");
// Render city name
out.println("<div>");
out.println("<span style='font-weight:bold;color:
#00628B'>City: </span>");
out.println("<span style='color: #00628B'>");
out.println(curLocation.getCity());
out.println("</span>");
out.println("</div>");
// Render county name
out.println("<div>");
out.println("<span style='font-weight:bold;color:
#00628B'>County: </span>");
out.println("<span style='color: #00628B'>");
out.println(curLocation.getCounty());
out.println("</span>");
out.println("</div>");
// Render state
out.println("<div>");
out.println("<span style='font-weight:bold;color:
#00628B'>State: </span>");
out.println("<span style='color: #00628B'>");
out.println(curLocation.getState());
out.println("</span>");
out.println("</div>");
// Render longitude
out.println("<div>");
out.println("<span style='font-weight:bold;color:
#00628B'>Longitude: </span>");
out.println("<span style='color: #00628B'>");
out.println(curLocation.getLongitude());
out.println("</span>");
out.println("</div>");
// Render latitude
out.println("<div>");
out.println("<span style='font-weight:bold;color:
#00628B'>Latitude: </span>");
out.println("<span style='color: #00628B'>");
out.println(curLocation.getLatitude());
out.println("</span>");
out.println("</div>");
out.println("</div>");
}
%>
|
この記事では、非同期通信および JSP TagLib コントロールの手法として、複合コントロールを作成する方法、カスタム・コントロール用にクライアント・サイドのイベント・フックを追加する方法、Ajax を利用して Web ページを一層動的にする方法などを学びました。この記事の土台となっているのは、連載のこれまでの 2 回の記事で説明した Ajax 通信の技術です。Ajax は、極めて動的かつスケーラブルでユーザー・フレンドリーな Web アプリケーションを作成することを可能にします。そして JSP TagLib コントロールを開発することによって、ビジネス分野のアプリケーションを構築する時間を短縮することができます。
この記事で説明したコントロールは、例えば以下のように拡張することができます。
- ポップアップ・ダイアログ・コントロールに表示オプションを追加して、例えばポップアップ・ダイアログ・ボックスの位置を指定する (センタリングする、また別のコントロールとの相対位置を指定するなど)。
- フェードイン/フェードアウト、スライド・インなどのアニメーションをポップアップ・ダイアログ・コントロールや更新パネル・コントロールに追加する。
- ポップアップ・ダイアログ・コントロールと更新パネル・コントロールに、例えば砂時計のようなロード中を表すアニメーション (コンテンツの取得が完了するのを待機している間のアニメーション) を追加する。
| 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|---|---|---|
| Source code for this article | ArticleCodeSample.zip | 2560KB | HTTP |
学ぶために
- 連載第 1 回目の記事「Ajax 対応の自動補完コントロール、カスケード・ドロップダウン・コントロールを作成する」(developerWorks、2008年9月) を読んでください。この記事では、他のフォームの値に応じて動的に HTML SELECT コントロールの値を設定するカスケード・ドロップダウン・コントロールの作成方法を説明しています。
- 連載第 2 回目の記事「自動入力コントロールとフィールド・バリデーター・コントロール」(2008年11月) を読んで、選択された値に基づいて動的にフォーム・フィールドを入力する Ajax 対応の自動入力コントロールの作成方法、そしてサーバー・サイドの検証を追加する Ajax 対応フィールド・バリデーターの作成方法を学んでください。
- Java で JSON を使用するためのオープンソースのライブラリー、JSON in Java の詳細を学んでください。
- W3C (W3C) Working Draft 15 April 2008 には、
XMLHttpRequestオブジェクトに提案される標準に関する貴重な情報が記載されています。 - 「Introducing JSON」を読んで、JSON の全容を理解してください。
- Mozilla による正式な JavaScript 言語の資料を参照してください。
製品や技術を入手するために
- The Zip Code Database Project は、無料の郵便番号データベースです。
- About.com: ZIP Code Database も同じく無料の郵便番号データベースです。
