目次


XPages で Dojo ウィジェットを利用する

IBM Lotus Notes/Domino のアプリケーション開発技術である「XPages」において、高機能 JavaScript ライブラリである「Dojo Toolkit」が提供するウィジェットを利用する方法

Comments

XPagesにおけるDojo Toolkit

XPagesは、Lotus Notes/Domino 8.5(以下、Lotus NotesはNotes、Lotus DominoはDominoと表記)から登場した、Notes/Domino上で動作するWebテクノロジーを利用したアプリケーションを開発するための技術です。その特徴の1つとして挙げられるのは、リッチなユーザー・インターフェースやAJAXによるタイプアヘッド、画面の部分更新などの機能をもつWeb2.0スタイルのアプリケーションを簡単に開発できる点です。こうしたWeb2.0スタイルのアプリケーションを実現するにあたって、XPagesではオープンソースJavaScriptライブラリであるDojo Toolkitが採用されており、XPages内部の実装でも利用されています。そのため、Dominoサーバーのインストール時には、Dojo Toolkitがデフォルトでインストールされるようになっています。カスタムインストールの場合は、XPages機能を利用するためには図1のようにDojoとXPagesを選択し、XPagesとともにDojo Toolkitも必ずインストールする必要があります。

図1. Dominoサーバーのカスタムインストール画面: XPagesとDojoを選択してインストール
図1. Dominoサーバーのカスタムインストール画面: XPagesとDojoを選択してインストール
図1. Dominoサーバーのカスタムインストール画面: XPagesとDojoを選択してインストール

こうしてインストールされたDojo Toolkitのブートストラップとなるスクリプトのdojo.jsには、リスト1に示したURLを使ってアクセスすることができます。先にも述べたように、Dojo ToolkitはXPages内部でも利用されているため、XPageでページを作成すると、明示的な指定をしなくてもこのdojo.jsがページにロードされるようになっています(図2)。

リスト1. Dominoサーバーのdojo.jsファイルのURL
// バージョン8.5の場合
http://<Dominoサーバーのホスト名>/domjs/dojo-1.1.1/dojo/dojo.js
// バージョン8.5.1の場合
http://<Dominoサーバーのホスト名>/domjs/dojo-1.3.2/dojo/dojo.js
図2. XPagesで作成したページのHTMLソース: dojo.jsがデフォルトで読み込まれている
図2. XPagesで作成したページのHTMLソース: dojo.jsがデフォルトで読み込まれている
図2. XPagesで作成したページのHTMLソース: dojo.jsがデフォルトで読み込まれている

このように、Dojo ToolkitはXPageで作成したページに自動的に読み込まれますが、XPagesが提供する標準コントロールや基本機能を利用してアプリケーションを開発する限りでは、Dojo Toolkitの存在はXPagesの中に完全に隠蔽されているため、開発者がDojo Toolkitの存在を意識する必要はありません。あくまでXPagesの範囲で開発を行えばよく、その結果として出来上がったページの中で内部的にDojo Toolkitが使われます。

Dojo Toolkitは非常に沢山の機能を提供しているため、Dojo Toolkitが提供している機能であっても、XPagesの標準機能としては提供されていないものも数多くあり、それらの機能を積極的に活用することも可能です。特に、Dojo Toolkitが提供するユーザー・インターフェースのコンポーネントであるDojoウィジェットは、種類が非常に豊富ですが、XPagesの標準コントロールの中で利用されているのは日時ピッカーやリッチテキストエディタなどに限られており、ほとんどのDojoウィジェットがXPagesの標準コントロールとしては提供されていません。本稿ではこうしたXPagesの標準コントロールとして提供されていないDojoウィジェットに焦点を当て、XPageの中で利用する方法について紹介していきます。

なお本稿ではXPagesおよびDojo Toolkitについての解説は行いませんが、詳しく知りたい方は本稿の最後にある参考文献リストに挙げたXPagesとDojo Toolkitの関連記事を参考にしてください。

XPagesでDojo Toolkitを利用するにあたっての注意点

XPagesでDojo Toolkitを利用するにあたって、注意すべき点が1つあります。それは、Dojo Toolkitのバージョンです。リスト1で示したdojo.jsのURLからも分かるように、Notes/Dominoのバージョンによって、インストールされるDojo Toolkitのバージョンが異なります。具体的には、以下の表1に示したバージョンのDojo Toolkitがそれぞれインストールされています。

表1. 各バージョンのDominoサーバーにインストールされているDojo Toolkitのバージョン
 Dominoバージョン8.5Dominoバージョン8.5.1
Dojo Toolkitのバージョン1.1.11.3.2

先にも述べたように、XPagesが提供する標準コントロールや基本機能を使用しているだけであれば、開発者はDojo Toolkitの存在を意識することがないため、Dojo Toolkitのバージョンも意識する必要はありません。しかし、これから紹介するDojoウィジェットをXPagesで使用する場合や、Dojo ToolkitのAPIをクライアントサイドJavaScriptから直接呼び出す場合には、提供されているDojoウィジェットやAPIなどがDojo Toolkitのバージョンによって異なるため、バージョンを意識しておく必要があります。また、Dominoサーバーのバージョンアップを行う場合には、それに伴ってDojo Toolkitのバージョンも上がる可能性があるため、アプリケーションの中で使用しているDojoウィジェットやAPIの互換性について事前にきちんと確認することが重要です。

Dojoウィジェット

Dojoウィジェットは、Dojo Toolkitが提供するユーザー・インターフェース(以下、UI)のコンポーネントです。ボタン(Button)、コンボボックス(ComboBox)といった基本的なウィジェットから、グラフを表示するChartingウィジェット、テーブル形式のデータ表示・編集機能を提供するGridウィジェット、ツリー形式のデータ表示を行うTreeウィジェットといった高機能なウィジェットまで、数多く提供されています(図3)。

図3. Dojoウィジェットの例
図3. Dojoウィジェットの例
図3. Dojoウィジェットの例

XPagesでのDojoウィジェットの利用法を紹介する前に、比較のために通常のHTMLページの中でDojoウィジェットを利用する方法を、図4のCalendarウィジェットの例で紹介します。この例では、Calendarウィジェットと共に入力フィールドを配置し、Calendarウィジェットの日付をクリックした際に、その日付が入力フィールドにセットされるようにします。

図4. Calendarウィジェットの例
図4. Calendarウィジェットの例

Calendarウィジェットに限らず、一般的にDojoウィジェットをWebページの中で利用する方法は2つあります。1つはHTMLマークアップの中で使用するDojoウィジェットを静的に宣言する方法、もう1つはJavaScriptのコードによって動的に生成する方法です。ここでは前者のHTMLマークアップで宣言する方法を使って実装します。図4のCalendarウィジェットのHTMLソースは以下のリスト2のようになります。

リスト2. Calendarウィジェットを利用するHTMLのソース例
<html>
<head>
<title>Calendarウィジェットサンプル</title>
<link rel="stylesheet" type="text/css" href="/domjs/dojo-1.3.2/dojo/resources/dojo.css">
<link rel="stylesheet" type="text/css" href="/domjs/dojo-1.3.2/dijit/themes/tundra/tundra.css"> <!-- b -->
<script type="text/javascript" src="/domjs/dojo-1.3.2/dojo/dojo.js"
    djConfig="parseOnLoad: true"></script> <!-- a -->
<script type="text/javascript">
dojo.require("dijit._Calendar");  // <!-- c -->
dojo.addOnLoad(function(){  // <!-- f -->
    dojo.connect(dijit.byId("calendar"), "onValueSelected", function(date) {
        var formattedDate = dojo.date.locale.format(date, {formatLength: "medium",selector: "date"});
        document.getElementById("selectedDate").value = formattedDate;
    });
});
</script>
</head>
<body class="tundra">
    <div> <!-- d,e -->
        <div id="calendar" dojoType="dijit._Calendar" dayWidth="narrow" style="width:200px"/>
    </div>
    <div><input id="selectedDate" type="text"/></div>
</body>
</html>

ポイントは以下の通りです。(見出しのアルファベットは、リスト2のコード内の<!-- -->で示した部分にそれぞれ対応しています)

  1. <script>タグでdojo.jsをロードします。このとき、HTMLマークアップの中でDojoウィジェットを宣言する場合には、djConfig=”parseOnLoad:true”という属性をつけておくと、dojoのロード時にHTMLマークアップがパースされて、指定したDojoウィジェットに変換されます。
  2. <link>タグでDojoウィジェットのテーマとなるCSSファイルを読み込みます。ここではtundraテーマを使用するためにtundra.cssを読み込み、bodyのclassにtundraを指定しています。
  3. <script>タグの中のJavaScriptで、必要なDojoウィジェットのクラスをdojo.require関数を使って読み込みます。ここではCalendarウィジェットのdijit._Calendarクラスを読み込んでいます。
  4. Calendarウィジェットを表示したい部分のHTMLマークアップでdojoType属性を指定し、Dojoウィジェットのクラス名を宣言します。ここではdojoType=”dijit._Calendar”としています。
  5. さらにそのDojoウィジェットに初期値として与えられる属性などがある場合には、同じ要素の中で属性を指定します。ここでは、dayWidth=”narrow”(カレンダーのヘッダー部分の曜日を短く表記)を例として指定しています。
  6. (オプション) 必要に応じて、作成したウィジェットを利用するためのロジックを実装します。ここでは、Calendarウィジェットで日付が選択されたときに呼び出されるonValueSelected関数に対してdojo.connectを行い、選択された日付をフォーマットして入力フィールドにセットするようにしています。

XPagesでDojoウィジェットを利用する

ここからはXPagesでDojoウィジェットを利用する方法を、サンプルを交えながら紹介していきます。本稿で紹介するのは、以下の2つのサンプルです。

  • サンプル1 (基本編): Calendarウィジェットを使用して、図4の例と同様のページをXPageで作成する
  • サンプル2 (応用編): Treeウィジェットを使用して、Dominoアプリケーション内に保存された社員の所属部門情報をツリー形式で表示し、さらにドラッグ&ドロップで所属部門を変更できるようにする

サンプルの完成版を含んだアプリケーションであるDojoWidgetSample.nsfは、本稿末尾のリンクからダウンロード可能ですのであわせてご覧ください。なお、このサンプルはNotes/Dominoのバージョン8.5.1をベースに作成しており、以下で紹介する手順もバージョン8.5.1での手順となります。

サンプル1. Calendarウィジェットを利用する

まず基本編として、図4で紹介したCalendarウィジェットの例と同様のページを、XPagesを使って実装します。ここではリスト2のサンプルコードで紹介したa~fのステップに沿って手順を紹介します。完成版はサンプルアプリケーションのCalendar.xspになります。

djConfig=”parseOnLoad:true”属性の追加、およびtundraテーマの読み込み

はじめに、リスト2のサンプルコードのポイントaとbの部分を実装します。本稿の最初で紹介したように、dojo.jsはXPageで作成したページにデフォルトで読み込まれていますが、djConfig=”parseOnLoad:true”属性はデフォルトでは付いていません。これを追加するには、XPageのデザイン画面上でキャンバス部分に相当する「XPage」コントロールを選択した状態でプロパティビューを開き、「すべてのプロパティ」タブの中の「基本」>「dojoParseOnLoad」プロパティをtrueにセットします(図5)。また、同じ「すべてのプロパティ」タブの中で、「基本」>「dojoTheme」プロパティをtrueにセットすると、tundraテーマがページに読み込まれるようになります(図5)。

図5. dojoParseOnLoadとdojoThemeプロパティをtrueに
図5. dojoParseOnLoadとdojoThemeプロパティをtrueに
図5. dojoParseOnLoadとdojoThemeプロパティをtrueに

dojo.require(“dijit._Calendar”) コードの追加

次に、リスト2のサンプルコードのポイントcのdojo.require(“dijit._Calendar”)部分を実装します。この部分はクライアントサイドのJavaScriptなので、スクリプトライブラリのクライアントサイドJavaScriptの中などでdojo.require(“dijit._Calendar”)というコードをそのまま書いても動作しますが、コードを書かずにプロパティで設定する方法があります。先ほどと同じく「XPage」コントロールのプロパティビューの「すべてのプロパティ」タブの中で、「基本」>「resources」プロパティで、「+」ボタンをクリックして「xp:dojoModule」を追加し、追加されたdojoModuleの「基本」>「name」プロパティにロードしたいDojoウィジェットのクラス名である「dijit._Calendar」を設定します(図6)。これにより、このXPageを表示した際のHTMLの中に、dojo.require(“dijit._Calendar”)というJavaScript文が埋め込まれるようになります。

図6. resourcesプロパティにdojoModuleを追加
図6. resourcesプロパティにdojoModuleを追加
図6. resourcesプロパティにdojoModuleを追加

HTMLマークアップのdojoType属性およびDojoウィジェット属性の追加

次に、リスト2のサンプルコードのポイントdとeのHTMLマークアップ部分を実装します。Calendarウィジェットの表示部分には、図7に示すように「パネル」コントロールを配置します。「パネル」コントロールは、表示されるHTMLの中では<div>要素になります。この「パネル」コントロールを選択してプロパティビューの「すべてのプロパティ」タブを開くと、「dojo」>「dojoType」というプロパティがあります。ここに、dojoType属性で指定していた「dijit._Calendar」というCalendarウィジェットのクラス名を指定します(図7)。また、同じ「すべてのプロパティ」タブの中の、「dojo」>「dojoAttributes」プロパティでDojoウィジェットの属性を設定することができます。ここではdayWidth=”narrow”という属性を追加するために、「dojoAttributes」プロパティで「+」ボタンをクリックして「dojoAttribute」を1つ追加し、「name」に「dayWidth」を、「value」に「narrow」を設定します(図7)。

図7. dojoTypeとdojoAttributeプロパティの設定(バージョン8.5.1のみ)
図7. dojoTypeとdojoAttributeプロパティの設定(バージョン8.5.1のみ)
図7. dojoTypeとdojoAttributeプロパティの設定(バージョン8.5.1のみ)

ここでは「パネル」コントロールを使用しましたが、dojoTypeおよびdojoAttributesプロパティを設定できるコントロールは他にも「ボタン」コントロール(<button>要素)、「スパンコンテンツ」コントロール(<span>要素)、「ブロックレベルコンテンツ」コントロール(<div>要素、「パネル」よりもシンプルで設定項目が少ない)などがあります。「スパンコンテンツ」コントロールと「ブロックレベルコンテンツ」コントロールは、デフォルトでは「コントロール」ビューに表示されていませんが、「その他…」を配置したときに表示される「コントロールの作成」ダイアログの中から選択することができます(図8)。

図8. 「スパンコンテンツ」コントロールと「ブロックレベルコンテンツ」コントロール
図8. 「スパンコンテンツ」コントロールと「ブロックレベルコンテンツ」コントロール
図8. 「スパンコンテンツ」コントロールと「ブロックレベルコンテンツ」コントロール

なお、このdojoTypeおよびdojoAttributeプロパティは、Notes/Dominoバージョン8.5.1のXPagesで新しく追加されたプロパティです。そのため、バージョン8.5では利用することができません。バージョン8.5では代わりに、<div dojoType=”dijit._Calendar dayWidth=”narrow”/>というタグをそのままHTMLとしてXPageに埋め込みます。埋め込む方法は、XPageを「ソース」で編集して埋め込む(図9)か、あるいは計算結果フィールドを利用して埋め込む(図10)かのどちらかになります。

図9. XPageを「ソース」で編集してHTMLを直接埋め込み(バージョン8.5)
図9. XPageを「ソース」で編集してHTMLを直接埋め込み(バージョン8.5)
図9. XPageを「ソース」で編集してHTMLを直接埋め込み(バージョン8.5)
図10. 計算結果フィールドのカスタム値としてHTMLを埋め込み(バージョン8.5)
図10. 計算結果フィールドのカスタム値としてHTMLを埋め込み(バージョン8.5)
図10. 計算結果フィールドのカスタム値としてHTMLを埋め込み(バージョン8.5)

dojo.connectを使ったJavaScriptロジックの追加

最後に、リスト2のサンプルコードのポイントfのdojo.connectによるJavaScriptロジック部分を実装します。XPageに<script>要素によるJavaScriptロジックを埋め込むには、「出力スクリプト」コントロールを使用します。「出力スクリプト」コントロールもデフォルトでは「コントロール」ビューに表示されていませんが、「その他…」を選択したときに表示される「コントロールの作成」ダイアログの中から選択することができます(図11)

図11. 「出力スクリプト」コントロールの配置
図11. 「出力スクリプト」コントロールの配置
図11. 「出力スクリプト」コントロールの配置

配置した「出力スクリプト」コントロールを選択した状態でプロパティビューを開き、「すべてのプロパティ」タブの中の「データ」>「value」プロパティの「値」フィールドからスクリプトエディタを起動し、リスト3に示したJavaScriptを入力します(図12)。このJavaScriptはリスト2のfのものとほぼ同じですが、以下の2点を変更しています。

  • dojo.addOnLoad関数の代わりに、XSP.addOnLoad関数を利用しています。dojo.addOnLoad関数はdojoがロードされてから呼び出されることが保証されていますが、XSP.addOnLoad関数は、dojoだけでなくXPage関連のライブラリもロードが完了してから実行されることが保証されます。
  • dijit.byId関数およびdocument.getElementById関数の引数で指定しているIDを、#{id:xxxx}という形式に変更しています。XPagesでは、各コントロールに対してDomino Designer上で設定した名前を元に、実際にHTMLとして表示される際のDOM IDが自動生成されます。この自動生成されたDOM IDを取得するために#{id:xxx}という記述を使用します。例えば、「calendar」という名前の「パネル」コントロールについて、表示時のDOM IDを取得するには「#{id:calendar}」と記述します。詳しくは、Domino Designerヘルプの「Lotus Domino Designer XPage および Eclipse ユーザーガイド」>「XPages のスクリプト記述」>「クライアントサイドのスクリプト」を参照してください。
図12. 「出力スクリプト」コントロールのvalueプロパティにJavaScriptロジックをセット
図12. 「出力スクリプト」コントロールのvalueプロパティにJavaScriptロジックをセット
図12. 「出力スクリプト」コントロールのvalueプロパティにJavaScriptロジックをセット
リスト3. 出力スクリプトで設定したdojo.connect部分のJavaScriptロジック例
XSP.addOnLoad(function(){
    dojo.connect(dijit.byId("#{id:calendar}"), "onValueSelected", function(date) {
        var formattedDate = dojo.date.locale.format(date, {formatLength: "medium",selector: "date"});
        document.getElementById("#{id:selectedDate}").value = formattedDate;
    });
});

以上でCalendarウィジェットのサンプルは完成です。Webブラウザを利用して完成したXPageにアクセスし、動作を確認してください。

サンプル2. Treeウィジェットを利用する

次に応用編として、TreeウィジェットをXPagesで利用する方法を紹介します。ここで紹介するサンプルは、図13のように、Treeウィジェットを使用してDominoアプリケーション内に保存された社員の所属部門情報をツリー形式で表示し、さらにドラッグ&ドロップで所属部門を変更する機能を実装しています。

図13. Treeウィジェットを利用したサンプル
図13. Treeウィジェットを利用したサンプル
図13. Treeウィジェットを利用したサンプル

Treeウィジェットをはじめとしたデータを可視化するDojoウィジェットでは、データの取得や更新をAJAXで行うことが多く、そのためのRESTサービスなどをサーバー側に用意するのが一般的です。そこで本サンプルでも、ツリー形式の社員所属部門情報の取得サービス、およびドラッグ&ドロップ時の所属部門更新サービスを作成し、TreeウィジェットからこれらのサービスにAJAXでアクセスして利用するようにします。

サンプル2-1. Treeウィジェットで表示するデータをJSONで返すサービスをXPageで作成する

はじめに、ツリー形式の社員所属部門情報を、Treeウィジェットで表示できるようなJSON形式のデータとして返すサービスを作成します。基本的にXPageはWebブラウザからアクセスされ、その上でページを表示するためのHTMLデータを返します。しかし実装を工夫することにより、HTMLデータを返さずにJSONやXMLを返すようにすることも可能です。この方法を利用して、XPageでJSONデータを返すサービスを作成してみます。完成版はサンプルアプリケーションのListEmployees.xspになります。

まず、「XPage」コントロールのプロパティビューの「すべてのプロパティ」タブの中で、「基本」>「rendered」プロパティをfalseにします(図14)。この設定により、表示のためのHTMLデータが何も出力されなくなります。この状態でブラウザによるプレビューを行うと真っ白な画面が表示され、HTMLのソースを見ると何のデータも返されていないことが分かります。

図14. XPageのrenderedプロパティをfalseにしてHTMLデータを返さないように設定
図14. XPageのrenderedプロパティをfalseにしてHTMLデータを返さないように設定
図14. XPageのrenderedプロパティをfalseにしてHTMLデータを返さないように設定

次にJSONデータを返す部分の実装を行います。「XPage」コントロールのイベントビューを開き、イベントリストの中の「ページ」>「afterRenderResponse」イベントの中で、リスト4のようにサーバーサイドJavaScriptを記述します(図15)。XPagesがJSF(Java Server Faces)の技術をベースにしていることを利用して、JSFが持っているレスポンスのデータを書き込むためのResponseWriterオブジェクトを取得して、JSONデータを文字列として直接書き出しています。

図15. XPageのafterRenderResponseイベント
図15. XPageのafterRenderResponseイベント
図15. XPageのafterRenderResponseイベント
リスト4. XPageのafterRenderResponse イベントでJSONデータを返すためのサーバーサイドJavaScript例
var extContext = facesContext.getExternalContext();

// ResponseのContentTypeおよびHeaderの指定
var response = extContext.getResponse();
response.setContentType("application/json");
response.setHeader("Cache-Control", "no-cache");

// ResponseのWriterにデータを書き込み
var writer = facesContext.getResponseWriter();

writeEmployeeTree(writer); // JSONデータの書き込み

writer.flush();
writer.endDocument();

リスト4のデータの書き込み部分で使用している「writeEmployeeTree(writer)」という関数はリスト5に示したもので、サンプルアプリケーションの中ではスクリプトライブラリ「tree.jss」に定義してあります。この関数は、図16に示した部門一覧ビューと社員一覧ビューのデータ(これらはあらかじめサンプルのNotesアプリケーション内に用意されています。)を元に、ツリー形式の社員所属部門情報をResponseWriterオブジェクトにJSON形式のデータとして出力します。

図16. 社員所属部門情報を生成するのに利用した部門一覧ビューと社員一覧ビュー
図16. 社員所属部門情報を生成するのに利用した部門一覧ビューと社員一覧ビュー
図16. 社員所属部門情報を生成するのに利用した部門一覧ビューと社員一覧ビュー
リスト5. JSONデータをwriterに書き込むサーバーサイドJavaScript例
// すべての社員のツリーデータを書き出す関数
function writeEmployeeTree(writer) {
    try {
        writer.append('{identifier:"id", label:"name", items:[');
        writeEmployeeTreeInDept(writer, "000"); // 最上位の部門はここでは'000'
        writer.append("]}");
    } catch (e) {
        print(e);
    }
}

// 指定された部門下の社員のツリーデータを書き出す関数
function writeEmployeeTreeInDept(writer, deptId) {
    // ビューから指定された部門の子部門および所属社員のCollectionを取得
    var deptCol = database.getView("DeptsByParentDept").getAllEntriesByKey(deptId);
    var empCol = database.getView("EmpsByDept").getAllEntriesByKey(deptId);

    // 子部門データの書き出し
    var deptEntry = deptCol.getFirstEntry();
    while (deptEntry != null) {
        var deptDoc = deptEntry.getDocument();
        var childDeptId = deptDoc.getItemValueString("deptID");
        var childDeptName = deptDoc.getItemValueString("deptName");
        writer.append('{id:"' + childDeptId + '", name:"' + childDeptName + '", type:"dept", ');

        // 子部門のさらに子供の要素を再帰的に書き出し
        writer.append("children:[");
        writeEmployeeTreeInDept(writer, childDeptId); // 再帰呼び出し
        writer.append("]}");

        deptEntry = deptCol.getNextEntry(deptEntry);
        if (deptEntry != null || empCol.getCount() > 0) writer.append(", ");
    }
    // 所属社員データの書き出し
    if (empCol.getCount() > 0) {
        var empEntry = empCol.getFirstEntry();
        while (empEntry != null) {
            var empDoc = empEntry.getDocument();
            var empID = empDoc.getItemValueString("employeeID");
            var empName = empDoc.getItemValueString("employeeName");
            writer.append('{id:"' + empID + '", name:"' + empName + '", type:"employee"}');

            empEntry = empCol.getNextEntry(empEntry);
            if (empEntry != null) writer.append(", ");
        }
    }
}

以上でサービスの実装は完成です。ListEmployees.xspにアクセスすると、リスト6に示すようなJSONデータが返ってきます。(リスト6の改行およびインデントはデータを見やすくするために後から手動で追加したもので、実際のサンプルでは改行やインデントがないデータが返されます。)

リスト6. ListEmployees.xspが実際に返すJSONデータ
{
    identifier:"id", label:"name", items:[
        {id:"100", name:"営業部", type:"dept", children:[
            {id:"110", name:"第1営業部", type:"dept", children:[
                {id:"111", name:"ソフトウェア営業部", type:"dept", children:[
                    {id:"0003", name:"営業三郎", type:"employee"},
                    {id:"0004", name:"営業四郎", type:"employee"}
                ]},
                {id:"112", name:"ハードウェア営業部", type:"dept", children:[
                    {id:"0005", name:"営業五郎", type:"employee"},
                    {id:"0006", name:"営業六郎", type:"employee"}
                ]},
                {id:"0002", name:"営業二郎", type:"employee"}
            ]},
            {id:"120", name:"第2営業部", type:"dept", children:[
                {id:"0007", name:"営業七郎", type:"employee"},
                {id:"0008", name:"営業八郎", type:"employee"}
            ]},
            {id:"0001", name:"営業一郎", type:"employee"}
        ]},
        {id:"200", name:"研究開発部", type:"dept", children:[
            {id:"210", name:"ソフトウェア開発部", type:"dept", children:[
                {id:"0010", name:"開発二郎", type:"employee"},
                {id:"0011", name:"開発三郎", type:"employee"}
            ]},
            {id:"220", name:"ハードウェア開発部", type:"dept", children:[
                {id:"0012", name:"開発四郎", type:"employee"},
                {id:"0013", name:"開発五郎", type:"employee"}
            ]},
            {id:"0009", name:"開発一郎", type:"employee"}
        ]}
    ]
}

サンプル2-2. Data APIと組み合わせてTreeウィジェットを利用する

次に、サンプル2-1で作成した表示用データを取得するためのサービスを利用して、Treeウィジェットによってツリー形式にデータを表示させるXPageを作成します。完成版はサンプルアプリケーションのTree1.xspになります。

作成したJSONデータ取得サービスへのアクセスにはDojo Toolkitが提供するData APIを使用します。ここで使用するのはdojo.data.ItemFileWriteStoreというData APIクラスで、url属性にサービスのURLを指定しておくことでAJAXによってデータを取得する機能を持っています。このData APIクラスを使ってサンプル2-1で作成したサービスにアクセスするTreeウィジェットを実装します。

通常のHTMLで実装する場合にはリスト7のようになります。Treeウィジェットの部分は「dijit.Tree」というクラスを使用しています。またData APIの部分も、HTMLマークアップによって指定することが可能です。Treeウィジェットの一般的な使用法について詳しく知りたい方は、こちらを参照してください。

リスト7. サンプル2-1で作成したサービスにData APIを通じてアクセスするTreeウィジェットのHTMLソース例(一部省略)
<script type="text/javascript">
dojo.require("dojo.data.ItemFileWriteStore");
dojo.require("dijit.tree.ForestStoreModel");
dojo.require("dijit.Tree");

function getIcon(item, opened) {
    try {
        return (!item) ? "deptIcon" : (treeStore.hasAttribute(item, "type")
                && "employee" == treeStore.getValue(item, "type")) ? "empIcon" : "deptIcon";
    } catch (e) {
        return "deptIcon";
    }
};
</script>

<body class=”tundra”>
    <div dojoType="dojo.data.ItemFileWriteStore" jsId="treeStore" url="ListEmployees.xsp"/>
    <div dojoType="dijit.tree.ForestStoreModel" jsId="treeModel" store="treeStore"
            rootId="root" childrenAttrs="children"/>
    <div class=”treeOuter”>
        <div dojoType="dijit.Tree" jsId="tree" model="treeModel"
                getIconClass="getIcon" showRoot="false"/>
    </div>
</body>

このリスト7の内容をXPageで実装する方法は、サンプル1のCalendarウィジェットの例の場合と同様です。dojo.require部分は「基本」>「resources」>「dojoModule」に、それ以外のJavaScriptは「出力スクリプト」コントロールに、dojoTypeとDojoウィジェット属性部分は「dojo」>「dojoType」および「dojoAttributes」に、それぞれ定義します。例えば、リスト7でのTreeウィジェットの本体であるdijit.Treeを指定した<div>要素の部分は、XPage上では「ブロックレベルコンテンツ」コントロールを使用して図17のように実装できます。

図17. Treeウィジェット表示部分の「ブロックレベルコンテンツ」コントロールのXPage上での設定
図17. Treeウィジェット表示部分の「ブロックレベルコンテンツ」コントロールのXPage上での設定
図17. Treeウィジェット表示部分の「ブロックレベルコンテンツ」コントロールのXPage上での設定

Treeウィジェット表示部分の実装は以上です。Tree1.xspをプレビューすると、図13に示したように社員所属部門情報がツリー形式で表示されるはずです。ここではまだドラッグ&ドロップはできません。

サンプル2-3. データ更新用のサービスをXPageで作成する

次に、Treeウィジェット上でのドラッグ&ドロップ操作時に利用する、社員所属部門の更新サービスをXPageで作成します。完成版はサンプルアプリケーションのUpdateEmployee.xspになります。

更新サービスの実装は、サンプル2-1で紹介したJSONデータ取得サービスの実装方法と似ています。異なるのは、afterRenderResponseイベントの中のサーバーサイドJavaScriptの実装のみです。ここではリスト8のように、社員IDと新しい所属部門ID がempId=0001&deptId=100というパラメータでPOSTされてきた際に、社員所属部門を更新する機能を実装します。サンプル2-1のJSONデータ取得サービスはHTTPアクセスのResponseを生成してデータを返すサービスでしたが、今回の更新サービスは、HTTPアクセスのRequestを受け取って処理を行うサービスとなっています。

リスト8. XPageのafterRenderResponse イベントでデータ更新サービスを実装したサーバーサイドJavaScript例
var extContext = facesContext.getExternalContext();

// Requestの取得
var request = extContext.getRequest();
if ("POST" == request.getMethod()) { // POST のみ許可
    // Postされたパラメータの取得
    var empId = param.get("empId");
    var deptId = param.get("deptId");
    if (empId && deptId) { // パラメータが指定されていれば
        var empDoc = database.getView("Emps").getDocumentByKey(empId);
        var deptDoc = database.getView("Depts").getDocumentByKey(deptId);
        if (empDoc && deptDoc) { // 実際に存在するか確認
            // 所属部門の更新
            empDoc.replaceItemValue("deptID", deptId);
            empDoc.save();
        }
    }
}

サンプル2-4. Treeウィジェット上でドラッグ&ドロップによりデータ更新できるようにする

最後に、Treeウィジェット上で社員ノードをドラッグ&ドロップ操作で移動可能にし、ドロップされた際にサンプル2-3で作成した社員所属部門の更新サービスをAJAXで呼び出し、データの更新を行うようにします。完成版はサンプルアプリケーションのTree2.xspになります。

ここでもまず通常のHTMLでの実装をベースにします。Treeウィジェット上でドラッグ&ドロップ操作を可能にするためには、通常のHTMLではリスト9の内容を追加する必要があります。Treeウィジェットでのドラッグ&ドロップ用にdijit._tree.dndSourceというクラスが用意されており、それを利用しています。Treeウィジェットでのドラッグ&ドロップの実装方法の詳細については、こちらを参照してください。

リスト9. Treeウィジェットで社員ノードのドラッグ&ドロップ操作を可能にするためのHTMLソース例(リスト7からの追加・変更部分のみ)
<script type="text/javascript">
dojo.require("dijit._tree.dndSource");

function dndAccept(source, nodes) {
    var type = "";
    var widget = dijit.getEnclosingWidget(nodes[0]);
    if (widget && widget.item) {
        type = treeStore.getValue(widget.item, "type");
    }
    return (type == "employee") ? true : false;
};

function itemTreeCheckItemAcceptance(node, source) {
    var type = "";
    var widget = dijit.getEnclosingWidget(node);
    if (widget && widget.item) {
        type = treeStore.getValue(widget.item, "type");
    }
    return (type == "dept") ? true : false;
};
</script>

<body class=”tundra”>
    <div dojoType="dojo.data.ItemFileWriteStore" jsId="treeStore" url="ListEmployees.xsp"/>
    <div dojoType="dijit.tree.ForestStoreModel" jsId="treeModel" store="treeStore"
            rootId="root" childrenAttrs="children"/>
    <div class=”treeOuter”>
        <div dojoType="dijit.Tree" jsId="tree" model="treeModel"
                getIconClass="getIcon" showRoot="false"
                dndController="dijit._tree.dndSource" checkAcceptance="dndAccept"
                checkItemAcceptance="itemTreeCheckItemAcceptance"/>
    </div>
</body>

このリスト9の内容もサンプル1やサンプル2-2のときと同様、XPageでの実装に変換可能です。例えば、dijit.Treeを指定した<div>要素の更新部分は、XPage上の「ブロックレベルコンテンツ」コントロールで図18のように更新されます。

図18. Treeウィジェット表示部分の「ブロックレベルコンテンツ」コントロールのXPage上での設定(更新版)
図18. Treeウィジェット表示部分の「ブロックレベルコンテンツ」コントロールのXPage上での設定(更新版)
図18. Treeウィジェット表示部分の「ブロックレベルコンテンツ」コントロールのXPage上での設定(更新版)

これで、Treeウィジェット上での社員ノードのドラッグ&ドロップによる移動が可能になりました。最後に、ドロップされた際に、サンプル2-3で実装したデータ更新サービスをAJAXで呼び出すように変更します。ドロップの際に処理を実行するために、リスト10のようにTreeウィジェットが持つdndControllerというオブジェクトのonDndDrop関数にdojo.connectし、この中でdojo.xhrPost関数を利用してデータ更新サービスを呼び出すようにします。

リスト10. dndControllerのonDndDrop関数にdojo.connectしてドロップ時にデータ更新サービスを呼び出すJavaScript例
XSP.addOnLoad(function() {
    dojo.connect(tree.dndController, "onDndDrop", function (source, nodes, copy) {
        var empId = null, deptId = null;
        // DropしたSourceのEmployeeノードから社員IDを取得
        var sourceWidget = dijit.getEnclosingWidget(nodes[0]);
        if (sourceWidget && sourceWidget.item) {
            empId = treeStore.getValue(sourceWidget.item, "id");
        }
        // DropされたTargetのDeptノードから部門IDを取得
        var targetWidget = dijit.getEnclosingWidget(this.current);
        if (targetWidget && targetWidget.item) {
            deptId = treeStore.getValue(targetWidget.item, "id");
        }
        // 変更要求をPOST
        if (empId && deptId) {
            dojo.xhrPost({
                url: "UpdateEmployee.xsp",
                postData: "empId=" + empId + "&deptId=" + deptId,
                load: function() {},
                error: function() {}
            });
        }
    });
});

以上でドラッグ&ドロップによるデータ更新の実装は完了です。図13に示したように、社員を別の部門にドラッグ&ドロップで移動すると、実装した機能によりDominoアプリケーション内の情報が更新されるため、ブラウザをリフレッシュしても更新されたデータが正しく表示されます。

まとめ

本稿では、DojoウィジェットをXPagesで利用する方法を、CalendarウィジェットとTreeウィジェットのサンプルを交えながら紹介しました。今回紹介したTreeウィジェットを含め、データを直感的でわかりやすい形で可視化することができるDojoウィジェットを利用すると、今までのDomino上で動作するWebアプリケーションでは実現が難しかった、Web2.0スタイルの見栄えと機能を備えたアプリケーションを開発することが可能になります。また、今回紹介したようなDojoウィジェット以外にも、境界線をドラッグ&ドロップで移動することができるSplitContainerや、アコーディオン形式で内容を表示するAccordionContainerなど、ページのレイアウトを調整するためのコンテナー機能を持ったDojoウィジェットなども数多くあります。これを機に、XPagesの中で最大限にDojoを活用した、よりリッチなXPagesアプリケーションの開発に取り組んでいただければ幸いです。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Lotus
ArticleID=469017
ArticleTitle=XPages で Dojo ウィジェットを利用する
publish-date=02262010