ファイルタイプフィールドの実装
次にFileTypeFieldのイベント処理を作成します。FileTypeFieldのJavaクラスは、それに対応したJavaScriptのクラスを作成することができます。FileTypeFieldはEnumOptionSingleSelectFieldから派生しているため、この状態ではFileTypeFieldに関する各種イベント処理は「EnumOptionSingleSelectField.js」で行われています。FileTypeFieldに独自のイベント処理を作成する場合、JavaScript側でも「EnumOptionSingleSelectField.js」 から派生した「FileTypeField.js」を作成します。
「FileTypeField.js」は[開発ツール/Webリソース作成]で作成できます。Webリソース作成ページでは、以下のようにArticlePageで使用されている[FileType]という文字列を含むクラスを検索します。表示された[FileTypeField]クラスのリンクをクリックし、表示された[Webリソース作成]のダイアログで [作成]ボタンを押下し、「FileTypeField.js」を作成します。
この操作で作成した「FileTypeField.js」は以下のようになります。
/**
* @fileOverview {@link FileTypeField}クラスを記述したファイルです。
*/
/**
* @class FileTypeField
*
* @extends EnumOptionSingleSelectField
*/
FileTypeField = createSubclass("FileTypeField", {}, "EnumOptionSingleSelectField");
/**
* HTMLエレメントとの対応付けを行います。
*/
FileTypeField.prototype.attach = function() {
EnumOptionSingleSelectField.prototype.attach.call(this);
};
「FileTypeField.js」にはattachというメソッドが定義されているだけです。このメソッドはページの初期化処理で呼ばれるので、このメソッドでイベントハンドラを登録します。以下の通りに「FileTypeField.js」を修正してください。
/**
* @fileOverview {@link FileTypeField}クラスを記述したファイルです。
*/
/**
* @class FileTypeField
*
* @extends EnumOptionSingleSelectField
*/
FileTypeField = createSubclass("FileTypeField", {}, "EnumOptionSingleSelectField");
/**
* HTMLエレメントとの対応付けを行います。
*/
FileTypeField.prototype.attach = function() {
EnumOptionSingleSelectField.prototype.attach.call(this);
var thisField = this;
this.get().change(function() {
thisField.id = $(this).attr("id");
thisField.showField($(this));
});
this.showField(this.get());
};
/**
* ファイルタイプに応じたフィールドを表示する。
* @param {jQuery} sel イベントが発生した要素。
*/
FileTypeField.prototype.showField = function(sel) {
var type = sel.val();
var tr = sel.parents("tr:first");
tr.find(".fileDiv").hide();
tr.find("." + sel.val()).show();
};
/**
* 設定されたファイルタイプに応じたフィールドを表示するように改造。
* @param {String} v 設定値。
*/
FileTypeField.prototype.setValue = function(v) {
EnumOptionSingleSelectField.prototype.setValue.call(this, v);
this.showField(this.get());
};
このソースのattachメソッドを見てください。this.get()というメソッドは、このフィールドに対応するjQueryオブジェクトを取得するメソッドです。このjQueryオブジェクトに対して、変更時のイベントハンドラを登録しています。イベントハンドラでは、まずフィールドIDの調整を行っています。この処理はEditableHtmlTableの行の入れ替え機能に対応するための処理です。行の入れ替えを行うと、HTMLの要素のIDを自動的にふり直します。そのため、対応するFileTypeFieldのインスタンスが保持するIDとずれてしまいます。なので、イベント処理の先頭でIDを合わせてやる必要があります。フィールドIDの調整後、FileTypeFieldクラスのshowFieldメソッドを呼び出しています。
showFieldメソッドは親となる<tr> ... </tr>からclass="fileDiv"と設定された要素を検索し、見つかったものを全て非表示に設定します。次に、FileTypeFieldで選択された値と一致するclassが指定された要素を表示するようになっています。つまり、画像が選択されたら<div class="fileDiv image"> ... </div>が表示されるという仕掛けです。この処理が適切に動作するようにするため「ArticleEditForm.html」の"attachFileTable"を以下の通りに修正します。
<div class="hScrollDiv">
<table id="attachFileTable" class="editableTable" style="width: 480px;">
<caption>添付ファイル</caption>
<tbody>
<tr>
<td style="width: 40px;">
<span id="attachFileTable[0].no"></span><br/>
<input type="button" id="attachFileTable[0].addButton" value="+"/><br/>
<input type="button" id="attachFileTable[0].deleteButton" value="-"/><br/>
</td>
<td style="width: calc(100% - 40px);">
<input type="hidden" id="attachFileTable[0].articleId" />
<input type="hidden" id="attachFileTable[0].fileId" />
<input type="hidden" id="attachFileTable[0].sortOrder" />
<input type="hidden" id="attachFileTable[0].createUserId" />
<input type="hidden" id="attachFileTable[0].createTimestamp" />
<input type="hidden" id="attachFileTable[0].updateUserId" />
<input type="hidden" id="attachFileTable[0].updateTimestamp" />
<select id="attachFileTable[0].fileType" style="width: 20%;"></select>
<label for="attachFileTable[0].fileComment">コメント:</label><input type="text" id="attachFileTable[0].fileComment" style="width: 50%;"/>
<div class="fileDiv file">
<input type="file" id="attachFileTable[0].attachFile" />
</div>
<div class="fileDiv image">
<input type="file" id="attachFileTable[0].attachImage" accept="image/*"/>
</div>
<div class="fileDiv video">
<input type="file" id="attachFileTable[0].attachVideo" accept="video/*"/>
</div>
<div class="fileDiv audio">
<input type="file" id="attachFileTable[0].attachAudio" accept="audio/*"/>
</div>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<th class="buttonColumn">
<input type="button" id="attachFileTable.addButton" value="+"/>
</th>
<th >
</th>
</tr>
</tfoot>
</table>
</div>
この修正で、ファイルタイプに対応したファイルフィールドのみ表示されるようになります。
初期化時やFileTypeFieldに値が設定された場合でも、ファイルフィールドの表示設定を行うため、showFieldメソッドをattachメソッドとsetValueメソッドで呼び出すようにします。
この修正の結果、ArticleEditFormは以下のようになります。
JavaScriptのクラスライブラリについて
ArticlePage.javaでは、BasePageから派生したArticlePageクラスに3種類のFormクラスのインスタンスを配置しています。また3種類のFormクラスでは、それぞれの機能に応じてFieldクラスやHtmlTableクラスのインスタンスが配置されています。
Page、Form、Field、HtmlTable等のJavaクラスは、全てWebComponentから派生しています。JavaScriptのクラスライブラリは、JavaのWebComponent以下の階層と同じ階層構造になっており、dataforms.controller.FormというJavaのクラスに対応するJavaScriptのクラスは 「/dataforms/controller/Form.js」というファイルで定義されています。Javaのクラスには、そのオブジェクトの構造やサーバー側の処理を記述し、それに対応するJavaScriptのクラスには、ブラウザ上で動作するイベント処理が記述されています。dataforms.jarは、Javaのクラスとそれに対応するJavaScriptのクラスが連携して動作するようになっています。
Javaのクラスに対応するJavaScriptのクラスが常に存在する必要があるかというと、必ずしもそうではありません。今回作成したFileTypeFieldを例にすると、「FileTypeField.java」を作成して選択肢を設定した段階では「FileTypeField.js」は存在しません。しかし、この状態でも列挙型管理で登録された選択肢が自動的に設定されます。これはFileTypeFieldの親クラスであるEnumOptionSingleSelectFieldクラスに対応する「EnumOptionSingleSelectField.js」が存在し、このスクリプトで選択肢の設定が行われているためです。
列挙型管理で登録した選択肢をプルダウンに設定するだけであれば、「FileTypeField.js」は作る必要がありません。しかし今回は「ファイルタイプに対応したFileフィールドのみを表示する」仕様にする必要があります。そのため「FileTypeField.js」を作成し、ファイルタイプが変更されたときのイベント処理を追加しています。
次に、ページのDataFormsServletが出力する初期化スクリプトを示します。
<script type="text/javascript">
<!--
$(function() {
var page = new BasePage();
page.csrfToken="vrgQf%2BNNL9azuIP898o5qj9PdZlu9Xu0RSP0aEc99CEIuXUA8pntnQ%3D%3D";
window.currentPage = page;
page.init();
page.attach();
});
-->
</script>
この初期化スクリプトでは1つのBasePageクラスのインスタンスを作成し、initとattachというメソッドを呼び出しています。var page = new BasePage();となっているのはArticlePageがBasePageクラスから派生し、「ArticlePage.js」が存在しないためです。Webリソース作成を使用して「ArticlePage.js」を作成すると、この初期化スクリプトはvar page = new ArticlePage();と変化し、 ArticlePage.jsに記述された初期化スクリプトが実行されるようになります。
初期化スクリプト中のpage.init()で、「ArticlePage.java」で定義したフォームやフィールドの親子関係を含む情報を取得し、これらのインスタンスを作成します。その後page.attach()を実行すると、Pageクラス中に含まれるWebComponentの各インスタンスのattachメソッドを呼び出します。このattachメソッドで、各WebComponentのインスタンスのIDを元に、HTMLの要素との対応付けを行います。特殊なイベント処理が必要な場合、このattachメソッドをオーバーライドしイベントハンドラを追加します。
新たに作成したJavaScriptクラスに機能を追加するということは、基本的にattachメソッドで新たなイベントハンドラを追加することになります。attach以外のメソッドを直接オーバーライドする場合もありますが、このようなメソッドは親クラスが登録したイベントハンドラから呼び出されています。WebComponent.prototype.get()メソッドは、各WebComponentのインスタンスに対応するjQueryのオブジェクトを取得します。WebComponent.prototype.find(selector)メソッドは、cssセレクタを指定して、対応するjQueryオブジェクトを取得します。これらのメソッドでjQueryオブジェクトを取得すれば、jQueryの機能でイベントハンドラを登録することや、各種制御を行うことができます。
また、WebComponent.prototype.getComponent(id)を使用すると、指定されたIDを持つ子となるWebComponentのインスタンスを取得することができます。このメソッドをPageやDialogで使用すればFormクラスのインスタンスを、Formクラスで使用すればFieldクラスやHtmlTableクラスのインスタンスを取得することができます。WebComponen.js以下のクラスには便利なメソッドがあります。そのメソッドを使用したい場合は、getComponentでWebComponentのインスタンスを取得し、そのインスタンスのメソッドを呼び出してください。
WebComponent.js以下の各種クラスが持つメソッドやプロパティについてはドキュメントの「7.Javascript API」を参照してください。
