!!![Clickのコントロール|http://incubator.apache.org/click/docs/controls.html]
Clickでは、クライアントサイドの出力およびサーバサイドの処理を行う数々の豊富なコントロールを提供しています。ここでは、以下の話題について述べていきます。
* [コントロールのインターフェース|click?page=Controls#p1] - コントロールのインターフェースについて
* [コントロールのコールバック|click?page=Controls#p2] - コントロールのイベントコールバックの仕組みについて
* [コントロールのクラス構成|click?page=Controls#p3] - コントロール関連のJavaクラス
* [メッセージプロパティ|click?page=Controls#p4] - コントロールのメッセージプロパティ
* [コンテナ|click?page=Controls#p8] - 他のコントロールをまとめることができるコントロールであるコンテナ
* [レイアウト|click?page=Controls#p11] - レイアウトオプションの説明とカスタムレイアウトの作成方法
ここでは、コントロール全般についての概略を扱います。個々のコントロールについては[[Clickが提供するコントロールの一覧|ControlList]]や[Javadoc|http://incubator.apache.org/click/docs/click-api/org/apache/click/control/package-summary.html]、[examples|http://incubator.apache.org/click/docs/examples.html]を参照してください。
!!コントロールのインターフェース
Clickのコントロールは、ユーザーの入力を処理するサーバサイドのコンポーネントを提供し、そしてユーザーへの表示を行います。Visual BasicのコントロールやDelphiのコンポーネントに相当するものなのです。
コントロールは、onProcessメソッドでユーザーの入力を処理し、toString()メソッドで、表示用HTMLを生成します。コントロールが処理され出力される一連の流れは以下の図1のようになります。
{{ref_image control-post-sequence-diagram.png}}
図1 ポストされたときの処理の流れ - created with Enterprise Architect courtesy [Sparx Systems|http://www.sparxsystems.com.au/]
Clickにおけるコントロールは、全てControlインターフェースを実装しなければなりません。
このControlインターフェースは下図2になります。
{{ref_image control-class-diagram.png}}
図2 Control インターフェース図 - created with Enterprise Architect courtesy [Sparx Systems|http://www.sparxsystems.com.au/]
Controlインターフェースのメソッドには以下のものがあります。
*[getContext()|http://incubator.apache.org/click/docs/click-api/org/apache/click/Control.html#getContext()] - リクエストの[Context|http://incubator.apache.org/click/docs/click-api/org/apache/click/Context.html]が取得できます。
*[getHtmlImports()|http://incubator.apache.org/click/docs/click-api/org/apache/click/Control.html#getHtmlImports()] - コントロールを使う際に必要となるHTMLヘッダーのimport文を定義します。
*[getId()|http://incubator.apache.org/click/docs/click-api/org/apache/click/Control.html#getId()] - HTML要素のid属性値を定義します。
*[setListener()|http://incubator.apache.org/click/docs/click-api/org/apache/click/Control.html#setListener(java.lang.Object,%20java.lang.String)] - コントロールのコールバックリスナーをセットします。
*[getMessages()|http://incubator.apache.org/click/docs/click-api/org/apache/click/Control.html#getMessages()] - コントロール用のローカライズされたメッセージマップを定義します。
*[getName()|http://incubator.apache.org/click/docs/click-api/org/apache/click/Control.html#getName()]/[setName()|http://incubator.apache.org/click/docs/click-api/org/apache/click/Control.html#setName(java.lang.String)] - ページモデルやフォームフィールドでの名前を定義します。
*[getParent()|http://incubator.apache.org/click/docs/click-api/org/apache/click/Control.html#getParent()]/[setParent()|http://incubator.apache.org/click/docs/click-api/org/apache/click/Control.html#setParent(java.lang.Object)] - コントロールの親コントロールを定義します。
*[onDeploy()|http://incubator.apache.org/click/docs/click-api/org/apache/click/Control.html#onDeploy(javax.servlet.ServletContext)] - Clickの起動時にコントロールに必要なファイルなどのリソースを配置するための設定を行います。
*[onInit()|http://incubator.apache.org/click/docs/click-api/org/apache/click/Control.html#onInit()] - イベントハンドラーの初期化を行います。
*[onProcess()|http://incubator.apache.org/click/docs/click-api/org/apache/click/Control.html#onProcess()] - リクエストのイベントハンドラを処理します。
*[onDestory()|http://incubator.apache.org/click/docs/click-api/org/apache/click/Control.html#onDestroy()] - イベントハンドラーの破棄処理を行います。
*[onDestory()|http://incubator.apache.org/click/docs/click-api/org/apache/click/Control.html#render(org.apache.click.util.HtmlStringBuffer)] - コントロールのHTML表現を生成します。
!!コントロールのコールバック
Clickのコントロールは、java.awt.ActionListenerコールバックに似たイベントコールバックの機構を提供します。
Clickは2つの方式のアクションリスナーをサポートします。1つ目はActionListenerインタフェースを利用してコンパイル時タイプセーフを行う方式、二つ目は[setListener(Object,String)|http://incubator.apache.org/click/docs/click-api/org/apache/click/Control.html#setListener(java.lang.Object,%20java.lang.String)]メソッドでコールバックメソッドの名前を指定する方式です。2つ目の方式はコード量が少なくなりますがタイプセーフではありません。
これら2つのアクションリスナー方式は以下のようになります:
{{code java
public class ActionDemo extends BorderPage {
// Uses listener style 1
public ActionLink link = new ActionLink();
// Uses listener style 2
public ActionButton button = new ActionButton();
public ActionDemo() {
// Verbose but provides compile time safety
link.setActionListener(new ActionListener() {
public boolean onAction(Control source) {
return onLinkClick(source);
}
});
// Succinct but typos will cause runtime errors
button.setListener(this, "onButtonClick");
}
// Event Handlers ---------------------------------------------------------
public boolean onLinkClick(Control source) {
..
return true;
}
public boolean onButtonClick() {
..
return true;
}
}
}}
すべてのコールバック関数はbooleanかjava.lang.Booleanを返す必要があります。
コールバック関数がtrueを返すと、他のコントロールの処理へと処理が続行されます。対してコールバック関数がfalseを返した場合、他のコントロールの処理はこれ以上実行されません。falseを返すことによってあなたは処理を中断し、別ページへのリダイレクトやフォワードによる遷移を効果的に行うことができます。
この実行の仕組みに関しては、[ページが実行される処理の流れ|click?page=Pages#p2]を参照してください。
処理の続行を停止し、別の処理をすることは非常に簡単です。
例えばonRender()で非常にコストのかかるデータベースの処理を実行するとしましょう。イベントハンドラでfalseを返すことで、この処理をスキップし別のページへフォワードすることができます。
!!コントロールのクラス構成
コントロール関連の主要なクラスは、[org.apache.click.control|http://incubator.apache.org/click/docs/click-api/org/apache/click/control/package-summary.html]パッケージで定義されています。このパッケージは、主要なHTML要素に対応するコントロールが含まれています。
拡張コントロール関連のクラスは、[org.apache.click.extras.control|http://incubator.apache.org/click/docs/extras-api/org/apache/click/extras/control/package-summary.html]パッケージで提供されています。Click Extrasのクラスは、サードパーティのフレームワークへの依存性を含むものもあります。
これらコントロールクラスのサブセットは下図3のように表すことができます。
{{ref_image control-package-class-diagram.png}}
図3 パッケージのクラス図 - created with Enterprise Architect courtesy [Sparx Systems|http://www.sparxsystems.com.au/]
このなかでも特に要となるのが以下のコントロールです。
* [[ActionLink]] - リスナを呼び出すことが可能なリンク
* [Field|http://incubator.apache.org/click/docs/click-api/org/apache/click/control/Field.html] - フォームのフィールドのためのabstractなコントロール.
* [[Form]] - 入力フォーム
* [[Submit]] - リスナを呼び出すことが可能な送信ボタン
* [[TextField]] - リスナを呼び出すことが可能なテキストボックス
これらのコントロールクラスは、動作をカスタマイズするために派生することを考慮に入れて設計されています。コントロールの全てのフィールドはprotectedとなっており、アクセサメソッドはpublicになっています。
またこれらのコントロールを組み合わせて、より複雑なコントロールを構築することも可能です。例えば[[CreditCardField]]は、[[Select]]コントロールを利用して複数のカードの種類を出力しています。
!!メッセージプロパティ
コントロールで使用するフィールドバリデーションメッセージとHTML整形文字列はプロパティファイルに切り出されます。このプロパティファイルを使用することによって、Clickアプリケーションを個々の言語用にローカライズすることができます。
!メッセージの解決
メッセージは、状況にあわせて取得できるよう、特定の順番で探索されます。
そのコントロールのもの、そのページのもの、アプリケーション全体のものとがあります。
その取得の順序は以下のようになります。
'''ページのメッセージ'''
まずページクラスに紐付けされているメッセージを探します。
Loginページの場合、そのメッセージプロパティは以下のファイルで定義されます。
/com/mycorp/page/Login.properties
特定のページでメッセージを調整したい場合には、ここに記述します。
'''ページ全体のメッセージ'''
次にアプリケーション全体のページに対して設定されているメッセージを探します。
/click-page.properties
アプリケーション全体のページで使いたいメッセージがあれば、ここに記述します。
'''コントロールのメッセージ'''
次にコントロールに対して紐付けされているメッセージを探します。
CustomTextFieldコントロールに対するメッセージは、以下のファイルに記述します。
/com/mycorp/control/CustomTextField.properties
'''アプリケーション全体のコントロールのメッセージ'''
ここまでのもので見つけられなかった場合、最後にアプリケーション全体のコントロールに対して
紐付けされているメッセージを探します。
アプリケーション全体のコントロールに対するメッセージは、以下のファイルに記述されます。
/click-control.properties
!コントロールのプロパティ
click-control.propertiesをカスタマイズする には、クラスパスにこのファイルを追加し、値を設定していくだけです。
このメッセージプロパティをカスタマイズする時には、設定したい値だけでなく、既定で設定してある以下の項目
すべてを記述する必要があります。
{{code java
# Click Control messages
field-maxlength-error={0} must be no longer than {1} characers
field-minlength-error={0} must be at least {1} characters
field-required-error=You must enter a value for {0}
file-required-error=You must enter a filename for {0}
label-required-prefix=
label-required-suffix=*
label-not-required-prefix=
label-not-required-suffix=
not-checked-error=You must select {0}
number-maxvalue-error={0} must not be larger than {1}
number-minvalue-error={0} must not be smaller than {1}
select-error=You must select a value for {0}
table-first-label=First
table-first-title=Go to first page
table-previous-label=Prev
table-previous-title=Go to previous page
table-next-label=Next
table-next-title=Go to next page
table-last-label=Last
table-last-title=Go to last page
table-goto-title=Go to page
table-page-banner={0} items found, displaying {1} to {2}.
table-page-banner-nolinks=
{0} items found, displaying {1} to {2}.
table-page-links=[{0}/{1}] {2} [{3}/{4}]
table-page-links-nobanner=[{0}/{1}] {2} [{3}/{4}]
table-no-rows-found=No records found.
table-inline-first-image=/click/paging-first.gif
table-inline-first-disabled-image=/click/paging-first-disabled.gif
table-inline-previous-image=/click/paging-prev.gif
table-inline-previous-disabled-image=/click/paging-prev-disabled.gif
table-inline-next-image=/click/paging-next.gif
table-inline-next-disabled-image=/click/paging-next-disabled.gif
table-inline-last-image=/click/paging-last.gif
table-inline-last-disabled-image=/click/paging-last-disabled.gif
table-inline-page-links=Page {0} {1} {2} {3} {4}
# Message displayed when a error occurs when the application is in "production" mode
production-error-message=
The application encountered an unexpected error.
}}
!メッセージへのアクセス
Fieldクラス及びその派生クラスは階層化されたバリデーションエラーメッセージなどの表示用メッセージに
紐付けされるメッセージリソースをサポートしています。
これらのローカライズされたメッセージは、以下のFieldのメソッドを通じてアクセス可能です。
* [getMessage(String)|http://incubator.apache.org/click/docs/click-api/org/apache/click/control/AbstractControl.html#getMessage(java.lang.String)]
* [getMessage(String, Object)|http://incubator.apache.org/click/docs/click-api/org/apache/click/control/AbstractControl.html#getMessage(java.lang.String,%20java.lang.Object)]
* getMessage(String, Object[])
* [getMessages()|http://incubator.apache.org/click/docs/click-api/org/apache/click/control/AbstractControl.html#getMessages()]
* [setErrorMessage(String)|http://incubator.apache.org/click/docs/click-api/org/apache/click/control/Field.html#setErrorMessage(java.lang.String)]
* [setErrorMessage(String, Object)|http://incubator.apache.org/click/docs/click-api/org/apache/click/control/Field.html#setErrorMessage(java.lang.String,%20java.lang.Object)]
これらのメソッドは、リクエストのLocaleを利用して、紐付けされた文字列リソースを探します。そしてMessageFormatで文字列の整形を行います。
!!Container
[Container|http://incubator.apache.org/click/docs/click-api/org/apache/click/control/Container.html]は他のコントロールを包含することができるコントロールで、コンポーネントの階層をまとめるものです。
Containerではコンポーネントを追加、削除したり他のコントロールを検索したりできます。
以下の一覧はContainerの例です:
* [Form|http://incubator.apache.org/click/docs/click-api/org/apache/click/control/Form.html] - デフォルトのフィールドレイアウトやエラーフィードバックを提供するHTMLフォーム。
* [Panel|http://incubator.apache.org/click/docs/click-api/org/apache/click/control/Panel.html] - ページに類似してこのContainerは自身のテンプレートとモデルを提供します。
* [FieldSet|http://incubator.apache.org/click/docs/click-api/org/apache/click/control/FieldSet.html] - 子コントロールの周りにボーダーを描画します。
これらのContainerは図4のように描かれます。
{{ref_image container-package-class-diagram.png}}
次に示すクラスはカスタムコンテナを作成するための便利な拡張ポイントを提供します:
*[AbstractContainer|http://incubator.apache.org/click/docs/click-api/org/apache/click/control/AbstractContainer.html]
*[AbstractContainerField|http://incubator.apache.org/click/docs/click-api/org/apache/click/extras/control/AbstractContainerField.html]
!AbstractContainer
カスタムコンテナを簡単に作成することが出来ます。例としてHTML divまたはspan要素を挙げます。
{{code java
public class Div extends AbstractContainer {
public Div(String name) {
super(name);
}
public String getTag() {
// Return the control's HTML tag.
return "div";
}
}
}}
新しく作成したコンテナを試すには以下のようにします:(このテストで使用しているMockContextについては[モックテストサポート|http://incubator.apache.org/click/docs/mock-api/overview-summary.html]ドキュメンテーションに記述されています)
{{code java
public class Test {
public static void main (String args[]) {
// Create mock context in which to test the container.
MockContext.initContext();
// Create a div instance called "mydiv"
String containerName = "mydiv";
Div mydiv = new Div(containerName);
// Add a control to the container
mydiv.add(new TextField("myfield"));
System.out.println(mydiv);
}
}
}}
この例を実行すると以下のような出力になります:
{{code xml
}}
!AbstractContainerField
AbstractContainerFieldはFieldを継承しContainerインタフェースを実装しています。これはFieldとContainerの両方が必要な場合に便利な基底クラスとして提供されます。
以下はAbstractContainerFieldを使用した例です:
{{code java
public class FieldAndContainer extends AbstractContainerField {
public FieldAndContainer(String name) {
super(name);
}
// Return the html tag to render
public String getTag() {
return "div";
}
}
}}
作成した新しいクラスのテストは以下のようなコード断片になります:
{{code java
public class Test {
public static void main (String args[]) {
// Create mock context in which to test the container.
MockContext.initContext();
// Create a FieldContainer instance called "field_container"
String containerName = "field_container";
FieldAndContainer fieldAndContainer = new FieldAndContainer(containerName);
// Add a couple of fields to the container
fieldAndContainer.add(new TextField("myfield"));
fieldAndContainer.add(new TextArea("myarea"));
System.out.println(fieldAndContainer);
}
}
}}
このコード断片の実行は以下のようになります:
{{code xml
}}
!!レイアウト
[Form|http://incubator.apache.org/click/docs/click-api/org/apache/click/control/Form.html]のようなコントロールはレイアウトとエラーレポートを自動で行います。そして多くの場合オートレイアウトは十分に良いです。
けれどもカスタムした、または複雑なレイアウトの場合、Formはいつもベストな選択肢ではありません。
カスタムレイアウトを作成するために2つのアプローチがあります。
+テンプレートアプローチ - Velocity、FreeMarkerまたはJSPのようなテンプレートエンジンを使用してレイアウトをHTMLマークアップのように宣言します。
+プログラム可能なアプローチ - Javaでカスタムレイアウトコンポーネントを構築します。このオプションはSwingを使用したコンポーネント構築と類似します。
!テンプレートレイアウト
テンプレートアプローチを選択するとページとレイアウトロジックをうまく分離することが出来ます。例:
{{code java
// EmployeePage.java
public EmployeePage extends Page {
private Form form;
public void onInit() {
// Create form
Form form = new Form("form");
// Add a couple of fields to the form
form.add(new TextField("firstname"));
form.add(new TextField("lastname"));
form.add(new IntegerField("age"));
form.add(new DoubleField("salary"));
// Add a submit button to form
form.add(new Submit("submit", "Add Employee"));
// Add form the page
addControl(form);
}
}
}}
DIV
とHTMLリストタグを使用したレイアウトを作成したい場合を想像してみます。
以下のようなテンプレートエンジンとしてVelocityを使用した'''employee.htm'''テンプレートを提供することが出来ます。
{{code html
${form.startTag()}
はかなり便利です。そのためClickはこのコンポーネントを提供していません。以下のように作成してみましょう:
{{code java
// HtmlList.java
// Create a list html element, that accepts
elements as children
public class HtmlList extends AbstractContainer {
public String getTag() {
return "ol";
}
// Can only add ListItems:
tags
public Control add(Control control) {
if (!(control instanceof ListItem)) {
throw new IllegalArgumentException("Only list items can be added.");
}
return super.add(control);
}
}
}}
{{code java
// ListItem.java
// Create a listItem
element
public class ListItem extends AbstractContainer {
public String getTag() {
return "li";
}
}
}}
以下の例で使用される別のコンポーネントは、指定されたFieldのHTMLラベル要素を描画するFieldLabelです。
{{code java
// FieldLabel.java
// Create an html