クライアント側の開発者向けガイド

     前  次    目次     
ここから内容

クライアント側の開発のベスト プラクティス

この章では、クライアント側のポータル コードを開発するためのヒントとベスト プラクティスについて説明します。この章では、次のトピックについて説明します。

 


ネームスペース

WLP 環境でブラウザベースの JavaScript コードを書く場合、注意を怠ると、ネームスペース衝突が発生します。この節では、複数のポートレットをひとつのページに配置した場合に発生する可能性のある JavaScript ネームスペース衝突の種類について説明します。

単純な動的テーブル ポートレット

以下の例では、JavaScript を使用して HTML テーブルに動的に行を追加します。Dojo ツールキットは、一次 UI 要素 (ボタン) とイベント処理メカニズムを提供するのに使用します。このようなコードをポータレットやポータル コンテキストで使用すると、問題が発生します。

注意 : 以下のコードは、表示依存関係ファイルを使用してポートレットにツールキット リファレンス、CSS リファレンス、および JavaScript 関数を追加するベスト、推奨プラクティスに基づいて設計されています。このテクニックの詳細および表示依存関係ファイルの作成と参照の手順については、「ポータル Web プロジェクトでの JavaScript ライブラリのコンフィグレーション」を参照してください。

コード リスト 6-1 に、表示依存関係ファイルを示します。表示依存関係ファイルは、ポートレットが必要とする外部 JavaScript と CSS などのページ レベルのイベントとリソースを定義する XML です。ファイルの全体のパターンは、「ポータル Web プロジェクトでの JavaScript ライブラリのコンフィグレーション」で説明されるパターンに従います。

コード リスト 6-2 に、JSP ファイルを示します。このファイルは、HTML テーブルに行を追加する JavaScript 関数を定義し、addRowsToTable() を呼び出す onClick イベントを開始する Dojo ボタンを含みます。

コード リスト 6-1 表示依存関係ファイル
<?xml version="1.0" encoding="UTF-8"?>
<window xmlns="http://www.bea.com/servers/portal/framework/laf/1.0.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.bea.com/servers/portal/framework/laf/1.0.0       laf-window-1_0_0.xsd">
<render-dependencies>
<html>
<links>
<search-path>
<path-element>../resources/js</path-element>
</search-path>
<link href="dijit/themes/tundra/tundra.css" type="text/css"
rel="stylesheet"/>
</links>
<scripts>
<search-path>
<path-element>../resources/js</path-element>
</search-path>
<script src="dojo/dojo.js" type="text/javascript"/>
<script type="text/javascript">
var djConfig = {parseOnLoad:true, isDebug:true};
</script>
</scripts>
</html>
</render-dependencies>
</window>
コード リスト 6-2 JSP ファイル
<script type="text/javascript">
// Dojo ボタン ウィジェットをロードする
dojo.require("dijit.form.Button");
</script>

<script type="text/javascript" >
    function addRowToTable() {
    var tbl = document.getElementById("table_1");
    var lastRow = tbl.rows.length;
    var row = tbl.insertRow(lastRow);

    //-- 左セルを更新する
    var cellLeft = row.insertCell(0);
    var node1 = document.createTextNode(lastRow);
    cellLeft.appendChild(node1);

    //-- 右セルを更新する
    var cellRight = row.insertCell(1);
    var node2 = document.createTextNode(lastRow);
    cellRight.appendChild(node2); }
    </script>

<button dojoType="dijit.form.Button" id="addRowButton">Add Row <script type="dojo/method" event="onClick">
addRowToTable();
</script></button>

<br>

<table border="1" id="table_1">
<tr>
<th colspan="3">Simple Dynamic Table</th>
</tr>
<tr>
<td>Column 1</td>
<td>Column 2</td>
</tr>
</table>

図 6-1 に、ポータル ページに追加した JSP ポートレットを示します。[Add Row] ボタンをクリックすると、新しい行がテーブルに追加され、行番号がテーブルの新しいセルに表示されます。

図 6-1 単純な動的テーブル ポータル

単純な動的テーブル ポータル

addRowToTable() JavaScript 関数は、テーブル ID を使用してテーブル要素を取得します。ポートレットはこのシナリオで正しく動作しますが、同じポータル ページに 2 つめの同一のポートレットを追加するとどうなるでしょう。

ネームスペースにおける衝突

ページに何回も動的テーブル ポートレットを追加すると、たとえば、3 つの動的テーブル ポートレットを含む以下のポータルを考慮します(図 6-2)。

図 6-2 ページ上の複数の動的テーブル ポートレット

ページ上の複数の動的テーブル ポートレット

現在のコンフィグレーションによって、JavaScript エラーが送出されます。ページ上の最初のボタン用のイベント ハンドラが登録されたら、登録の後続試行は失敗します。以下のようなエラー メッセージが表示されます。

Exception... "'Error: Tried to register widget with id==helloButton but that id is already registered' ...

このエラーの結果、最初のボタンのみ登録され、操作できます。

複数の動的テーブル ポートレットがポータル ページで正しく動作しなかった理由を理解することで、ポータル開発者が直面する一般的なブラウザ側のプログラミング問題を避けることができます。

ポータル ページのために生成される HTML ソース コードを見ると、ポートレットが複製するとき動的テーブルが適切に機能しない理由が分かります。サーバ上のポータル フレームワークは、各ポートレットのために同じ HTML マークアップを返します。図 6-3 では、同じ HTML ページに JavaScript ブロックと HTML テーブル コードの両方を一字一句三回複製する例を示します。WebLogic ポータル フレームワークでは、ポートレットにユニークな ID が割り当てられます。ただし、表示された各ページ用のすべての JSP と HTML コードが同じです。

図 6-3 3 つの同一のポートレットがあるページでのソース コードの反復

3 つの同一のポートレットがあるページでのソース コードの反復

ミステリアスなテーブル行

コード リスト 6-2 の dijit ボタンにポートレット ID をプレフィックスすることなどで、ユニークな名前を割り当てることができます (「アドホック ネームスペースの使用」で説明するテクニックを参照)。このテクニックを使用すると、エラー状態を回避することができますが、ボタン ID にユニークな名前を割り当てるだけでネームスペースの問題を解決できるということはありません。

JavaScript 関数ブロックが何回も含む場合、複製の変数は宣言されるたびに、すぐ次の変数によって上書きされます。この例では、addRowButton 変数は三回宣言されます。(スクリプト ブロックがページに三回含められているからです)。したがって、図 6-4 に示すように、どんなボタンをクリックしても 1 番目のテーブルにのみ影響が与えられます (行が追加される)。

図 6-4 誤ったテーブルにミステリアスに追加された行

誤ったテーブルにミステリアスに追加された行

このネームスペースに関する問題を回避するテクニックについては、次の「ネームスペースにおける衝突の回避」節で説明します。

 


ネームスペースにおける衝突の回避

この節では、「ネームスペースにおける衝突」で説明したネームスペースにおける衝突を回避するためのテクニックとベスト プラクティスについて説明します。テクニックには、以下のことを含みます。

アドホック ネームスペースの使用

クライアント側のブラウザ コードでネームスペースにおける衝突を回避する 1 つの方法は、アドホック ネームスペースを使用することです。アドホック ネームスペースとは、以下のことにユニークな名前を割り当てることです。

たとえば、ネームスペースにおける衝突に一覧表示された JSP を書き換えて、グローバル関数、変数、および ID を全てユニークにして、特定のポートレットをスコープとすることができます。これをポートレット内に実行する 1 つの簡単な方法は、コード リスト 6-3 に示すように、ポートレットのインスタンス ラベルを適切なグローバル JavaScript 識別子に追加することです。このコード サンプルへの追加は、強調表示されています。このサンプルは、PortletPresentationContext オブジェクトからポートレット インスタンス ラベルを取得することに依存します。この ID は、ポータルに出る個々のポートレットのためにユニークです。JSP の式を使用して、ポートレット ラベルを適切なグローバルな識別子に追加されます。この場合、HTML テーブル ID、グローバルな関数 addRowToTable()、ボタン ウィジェット ID、およびボタン ウィジェット変数をユニークでネームスペースする必要があります。

ヒント : 関数内の JavaScript 変数のスコープはローカルであるので、グローバル コンテキスト用にネームスペースする必要はありません。
コード リスト 6-3 アドホック ネームスペースがある動的テーブル JSP
<%@ page import="com.bea.netuix.servlets.controls.portlet.PortletPresentationContext"%>
<%
// ヒント: このスクリプトレットの代わりに JSP タグ  <render:encodeName name="someName" .../> を使用して、
// 同じタスクを達成できる
// (ポートレット インスタンス ラベルの取得)
PortletPresentationContext portletCtx = (PortletPresentationContext) PortletPresentationContext.getPortletPresentationContext(request);
String portletId = portletCtx.getInstanceLabel();
pageContext.setAttribute("prefix", portletCtx.getInstanceLabel());
%>

<script type="text/javascript">
//Dojo ボタン ウィジェットをロードする
dojo.require("dijit.form.Button");
</script>

<script type="text/javascript" >
function ${prefix}_addRowToTable() {
var tbl = document.getElementById("${prefix}_table_1");
var lastRow = tbl.rows.length;
var row = tbl.insertRow(lastRow);

//-- 左セルを更新する
var cellLeft = row.insertCell(0);
var node1 = document.createTextNode(lastRow);
cellLeft.appendChild(node1);

//-- 右セルを更新する
var cellRight = row.insertCell(1);
var node2 = document.createTextNode(lastRow);
cellRight.appendChild(node2);
}
</script>

<button dojoType="dijit.form.Button" id="${prefix}_helloButton"> Add Row
<script type="dojo/method" event="onClick">
${prefix}_addRowToTable();
</script>
</button>

<br>

<table border="1" id="${prefix}_table_1">
<tr>
<th colspan="3">Simple Dynamic Table</th>
</tr>
<tr>
<td>Column 1</td>
<td>Column 2</td>
</tr>

</table>

この JSP を使用してポートレットを作成し、ポータルに何回も追加すると、図 6-5 に示すように、ポートレットが独立して正しく機能します。

図 6-5 ネームスペースされたポートレット コードは正しく機能する

ネームスペースされたポートレット コードは正しく機能する

アドホック ネームスペースは、ブラウザ コードのあるポートレットが正しく機能することを確認する効果的な方法ですが、以下の節では他のテクニックとベスト プラクティスについて説明します。

書き換えトークンの使用

JavaScript 開発者には最も知られているベスト プラクティスは、関数を .js ファイルに外部化することです。JavaScript 関数を外部化し、WLP の表示依存関係機能と書き換えトークンを使用して変数にユニークなスコープを提供する方法は、「アドホック ネームスペースの使用」で説明したアドホック ネームスペース方法に代わるものです。書き換えトークンの詳細については、『ポートレット開発ガイド』の「ポートレットの依存関係」を参照してください。

最初の手順は、ネームスペースを必要とする スクリプト ブロックを外部の .js ファイルに配置することです。その後、このファイルを表示依存関係ファイルから参照します。

ヒント : .js ファイルは、<script src ...> タグではなく、<script content-uri ...> タグを使用して含めます。src タグは最も標準のメカニズムですが、それを使用すると、書き換えトークンは展開されません。依存関係ファイルの作成および依存関係ファイルのポートレットへの追加の詳細については、『ポートレット開発ガイド』の「ポートレットの依存関係」を参照してください。

コード リスト 6-4 は、表示依存関係ファイルのサンプルを示します。追加された .js ファイルは太字で強調されています。

コード リスト 6-4 表示依存関係ファイル
<?xml version="1.0" encoding="UTF-8"?>
<window xmlns="http://www.bea.com/servers/portal/framework/laf/1.0.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.bea.com/servers/portal/framework/laf/1.0.0       laf-window-1_0_0.xsd">
<render-dependencies>
<html>
<links>
<search-path>
<path-element>../resources/js</path-element>
</search-path>
<link href="dijit/themes/tundra/tundra.css" type="text/css"                   rel="stylesheet"/>
</links>
<scripts>
<search-path>
<path-element>../resources/js</path-element>
</search-path>
                <script src="dojo/dojo.js" type="text/javascript"/>
<script type="text/javascript">
var djConfig = {parseOnLoad:true, isDebug:true};
</script>
               <script type=
                 "text/javascript" content-uri="dojotest.js"/>
</scripts>
</html>
</render-dependencies>
</window>

外部の .js ファイルのコンテンツをコード リスト 6-5 に示します。wlp_rewrite_ は、ページ内でのユニークな名前を必要とするすべての関数名と識別子に追加されます。

コード リスト 6-5 書き換えトークンがある外部 dojotest.js ファイルのコンテンツ

function wlp_rewrite_addRowToTable() {
var tbl = document.getElementById("wlp_rewrite_table_1");
var lastRow = tbl.rows.length;
var row = tbl.insertRow(lastRow);

//-- 左セルを更新する
var cellLeft = row.insertCell(0);
var node1 = document.createTextNode(lastRow);
cellLeft.appendChild(node1);

//-- 右セルを更新する
var cellRight = row.insertCell(1);
var node2 = document.createTextNode(lastRow);
cellRight.appendChild(node2);
}

ポータル フレームワークによって、このトークンは、ユニークな識別子であるポートレットのインスタンス ラベルに置き換えられます。

コード リスト 6-6 は、JavaScript 関数を取り除除いた JSP ファイルを示します。.js ファイルは表示依存関係ファイルに含まれる場合、JSP ファイルにインポートする必要はありません。JSP では、ユニークなポートレット ID がある dijit ボタン ID とテーブル ID のネームスペースがまだ必要です。

コード リスト 6-6 JSP ファイル
<%@ page import="com.bea.netuix.servlets.controls.portlet.PortletPresentationContext"%>
<%
PortletPresentationContext portletCtx = (PortletPresentationContext) PortletPresentationContext.getPortletPresentationContext(request);
String portletId = portletCtx.getInstanceLabel();
pageContext.setAttribute("prefix", portletCtx.getInstanceLabel());
%>

<script type="text/javascript">
// Dojo ボタン ウィジェットをロードする
dojo.require("dijit.form.Button");
</script>

<button dojoType="dijit.form.Button" id="${prefix}_helloButton"> Add Row
<script type="dojo/method" event="onClick">
${prefix}_addRowToTable();
</script>
</button>

<br>

<table border="1" id="${prefix}_table_1">
<tr>
<th colspan="3">Simple Dynamic Table</th>
</tr>
<tr>
<td>Column 1</td>
<td>Column 2</td>
</tr>

</table>

JavaScript 関数のパラメータ化

この節では、パラメータ化された JavaScript 関数が .js ファイルで因数分解される動的テーブル サンプルの他のバージョンについて説明します。コード リスト 6-7 には、この外部ファイルを示します。addRow() と init() の両方の関数はパラメータ化されています。これは、表示依存関係ファイルを使用して、外部 .js ファイルをポートレットに含めるためのベスト プラクティスです。「表示依存関係ファイルの作成」を参照してください。

コード リスト 6-7 外部 JavaScript ファイル (.js)
function addRow(id) {

var table = document.getElementById(id+"_table");
var numrows = table.getElementsByTagName("tr").length;
var row = table.insertRow(numrows);

//-- 行のセルにテキストを追加する。
var cellLeft = row.insertCell(0);
var textLeft = document.createTextNode(numrows-1);
cellLeft.appendChild(textLeft);

var cellRight = row.insertCell(1);
var textRight = document.createTextNode(numrows-1);
cellRight.appendChild(textRight);

}
function initialize(id) {
var addHandler =
function() {
addRow(id);
};

var addRowButton = dijit.byId(id+"_addRowButton");
dojo.connect(addRowButton, 'onClick', addHandler);
}

init() 関数に ID パラメータを渡すよう、ポートレットの生成元である JSP ファイルを書き込む必要があります。addOnLoad() 関数は、init() 関数を返す匿名関数を渡すことで、呼び出します。このテクニックは、必要です。このテクニックを使用すると、init() に渡されたパラメータは addOnLoad() 関数が値を返した後も永続化できます。

コード リスト 6-8 変更された JSP ファイル
<%@ page import="com.bea.netuix.servlets.controls.portlet.PortletPresentationContext"%>
<%
PortletPresentationContext portletCtx = (PortletPresentationContext) PortletPresentationContext.getPortletPresentationContext(request);
pageContext.setAttribute("prefix", portletCtx.getInstanceLabel());
%>

<script type="text/javascript">
// Dojo ボタン ウィジェットをロードする
dojo.require("dijit.form.Button");
</script>

<script type="text/javascript">
dojo.addOnLoad( function() {
initialize("${prefix}");
});
</script>


<button dojoType="dijit.form.Button" id="${prefix}_addRowButton"> Add Row
</button>

<br>

<table border="1" id="${prefix}_table">
<tr>
<th colspan="3">Simple Dynamic Table</th>
</tr>
<tr>
<td>Column 1</td>
<td>Column 2</td>
</tr>

</table>

つまり、「単純な動的テーブル ポートレット」で説明するオリジナルの動的ポートレットは、ポータル/ポートレット環境で機能するように再設計されています。最初は、ネームスペースにおける衝突を防ぐように、グローバル変数と関数はアドホック ネームスペースで指定しました。次に、JavaScript コードは .js ファイルに外部化されました。これを実行するため、関数がパラメータ化され、閉鎖と匿名関数テクニックが使用されます。もちろん、JavaScript コードの外部化では、コードをが再利用でき、JSP や HTML ファイルにおける混乱を減少させることができるので、ベスト プラクティスです。

Disc API の使用

Disc API を使用して、JavaScript 変数をスコープするのに使用できるユニークなポートレット ラベルを取得できます。基本テクニックは「アドホック ネームスペースの使用」で説明するテクニックと同じです。Disc を使用して、ポートレットのコンテキスト オブジェクトからポートレット ラベルを取得できます。詳細については、「REST と Disc の使用」を参照してください。


  ページの先頭       前  次