トップ 一覧 ヘルプ RSS ログイン

Controlsの変更点

  • 追加された行はこのように表示されます。
  • 削除された行はこのように表示されます。
!!![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=<span class="required">*</span>
label-not-required-prefix=
label-not-required-suffix=&nbsp;

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=<span class="pagebanner">{0} items found, displaying {1} to {2}.</span>
table-page-banner-nolinks=
  <span class="pagebanner-nolinks">{0} items found, displaying {1} to {2}.</span>
table-page-links=<span class="pagelinks">[{0}/{1}] {2} [{3}/{4}]</span>
table-page-links-nobanner=<span class="pagelinks-nobanner">[{0}/{1}] {2} [{3}/{4}]</span>
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=
  <div id='errorReport' class='errorReport'>The application encountered an unexpected error.
  </div> 
}}

!メッセージへのアクセス

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
<div name="mydiv" id="mydiv"> 
    <input type="text" name="myfield" id="myfield" value="" size="20" /> 
</div>
}}

!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
<div name="field_container" id="field_container"> 
    <input type="text" name="myfield" id="myfield" value="" size="20"/> 
    <textarea name="myarea" id="myarea" rows="3" cols="20"></textarea> 
</div>
}}
!!レイアウト
[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<div>とHTMLリスト<ol>タグを使用したレイアウトを作成したい場合を想像してみます。
以下のようなテンプレートエンジンとしてVelocityを使用した'''employee.htm'''テンプレートを提供することが出来ます。
{{code html
<!-- employee.htm --> 
${form.startTag()} 
    <div style="margin: 1em;"> 
        <ol> 
            <li> 
                <label for="firstname">Firstname:</label> 
                ${form.fields.firstname} 
            </li> 
            <li> 
                <label for="lastname">Lastname:</label> 
                ${form.fields.lastname} 
            </li> 
            <li> 
                <label for="age">Age:</label> 
                ${form.fields.age} 
            </li> 
            <li> 
                <label for="salary">Salary:</label> 
                ${form.fields.salary} 
            </li> 
        </ol> 
    </div> 
    ${form.fields.submit} 
${form.endTag()}
}}
CSSスタイルシートを使用して上記のマークアップを装飾された見た目のFormに変換することができます。

テンプレートアプローチを使用することには賛否両論あります。

テンプレートアプローチの1つの利点は、レイアウトは明白で必要であれば調整が簡単なことです。例としてdivとオーダーリストを使用する代わりに、テンプレートを変更するだけでテーブルレイアウトに影響を与えます。

テンプレートアプローチの欠点は、冗長なことです。

上記の例ではJavaでフィールドを作成し、それらをテンプレートにマークアップで記述しています。

もし例に新しいフィールドを追加する要件が発生した場合、Pageクラスにもテンプレートにもフィールドを追加する必要があります。

大抵の場合、テンプレートエンジンを使用して'''汎用化された'''レイアウトを利用できます。[Macro.vm|http://incubator.apache.org/click/docs/click-api/org/apache/click/control/Form.html#velocity-macros]はVelocityを使用した汎用Formレイアウトの例です。

!プログラム化レイアウト
テンプレートアプローチで紹介した冗長性と戦う場合、プログラム化アプローチを採用して、普通のJavaといくつかのClickクラス群を使用してカスタムレイアウトを構築できます。

Click extras では[HtmlForm|http://incubator.apache.org/click/docs/click-api/org/apache/click/extras/control/HtmlForm.html]と[HtmlFieldSet|http://incubator.apache.org/click/docs/click-api/org/apache/click/extras/control/HtmlFieldSet.html]という名前のこの条件に当てはまる2つの役立つクラスを提供しています。

FormやFieldSetが描画で使用するTableレイアウトと違い、HtmlFormとHtmlFieldSetはそれに追加されたコントロールを順に描画し、どんな余分なマークアップも追加しません。HtmlFormの例は以下のようになります。

カスタムレイアウトを作成する場合、HTMLコンストラクトList<ul>はかなり便利です。そのためClickはこのコンポーネントを提供していません。以下のように作成してみましょう:
{{code java
// HtmlList.java 
// Create a list <ol> html element, that accepts <li> elements as children 
public class HtmlList extends AbstractContainer { 
 
    public String getTag() { 
        return "ol"; 
    } 
 
    // Can only add ListItems: <li> 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 <li> element 
public class ListItem extends AbstractContainer { 
 
    public String getTag() { 
        return "li"; 
    } 
}
}}
以下の例で使用される別のコンポーネントは、指定されたFieldのHTMLラベル要素を描画するFieldLabelです。
{{code java
// FieldLabel.java 
// Create an html <label> element for a specified Field 
public class FieldLabel extends AbstractControl { 
 
    private Field target; 
 
    private String label; 
 
    public FieldLabel(Field target, String label) { 
        this.target = target; 
        this.label = label; 
    } 
 
    public String getTag() { 
        return "label"; 
    } 
 
    // Override render to produce an html label which produces: 
    // Firstname: 
    public void render(HtmlStringBuffer buffer) { 
        // Open tag: <label 
        buffer.elementStart(getTag()); 
 
        // Set attribute to target field's id 
        setAttribute("for", target.getId()); 
 
        // Render the labels attributes 
        appendAttributes(buffer); 
 
        // Close tag: <label for="firstname"> 
        buffer.closeTag(); 
 
        // Add label text: <label for="firstname">Firstname: 
        buffer.append(label); 
 
        // Close tag: <label for="firstname">Firstname:</label> 
        buffer.elementEnd(getTag()); 
    } 
 
}
}}
これでフォームを組み立てることが出来ます。

テンプレートアプローチのemployeeの例で続けると、もう一度EmployeePageを作成します。ただし、今回はHtmlFormとHtmlListをカスタムレイアウト作成のために使用します:
{{code java
// EmployeePage.java 
public class EmployeePage extends Page { 
    // A form instance variable 
    private HtmlForm form; 
 
    // Build the form when the page is initialized 
    public void onInit() { 
        // Create an HtmlForm which is ideal for composing manual layouts 
        form = new HtmlForm("form"); 
         
        // Create a list and add it to the form.  
        HtmlList list = new HtmlList(); 
        form.add(list); 
         
        // Add firstname field and pass in its name, label and the list to add the field to 
        addTextField("firstname", "Firstname:", list); 
        addTextField("lastname", "Lastname:", list); 
        addTextField("age", "Age:", list); 
        addTextField("salary", "Salary:", list); 
         
        // Add a submit button to form 
        form.add(new Submit("submit", "Add Employee")); 
 
        // Add the form to the page 
        addControl(form); 
    } 
     
    // Provide a helper method to add fields to the form 
    private void addTextField(String nameStr, String labelStr, List list) { 
        // Create a new ListItem <li> and add it to the List 
        ListItem item = new ListItem(); 
        list.add(item); 
 
        // Create a textfield with the specified name 
        Field field = new TextField(nameStr); 
         
        // Create a field label, which associates the label with the field id. 
        // label.toString would output: <label for="firstname">Firstname:</name> 
        FieldLabel label = new FieldLabel(field, labelStr); 
 
        // Next add the label and field to the list item. 
        // item.toString would then produce: 
        // <li> 
        //   <label for="firstname">Firstname:</name> 
        //   <input type="text" name="firstname" id="form_firstname" value="" size="20"/> 
        // </li> 
        // 
        item.add(label); 
        item.add(field); 
    } 
}
}}
今回は'''employee.htm'''テンプレートはトップレベルコンポーネントの名前だけ記述すればよく、このケースでは'''form'''です。
{{code html
<!--employee.htm--> 
${form}
}}

これで以下のマークアップが提供されます。
{{code html
<!-- employee.htm --> 
<form method="post" id="form" action="/myapp/employee.htm"> 
<input type="hidden" name="form_name" id="form_form_name" value="form"/> 
    <ol> 
        <li> 
            <label for="firstname">Firstname:</label> 
            <input type="text" name="firstname" id="form_firstname" value="" size="20"/> 
        </li> 
        <li> 
            <label for="lastname">Lastname:</label> 
            <input type="text" name="lastname" id="form_lastname" value="" size="20"/> 
        </li> 
        <li> 
            <label for="age">Age:</label> 
            <input type="text" name="age" id="form_age" value="" size="20"/> 
        </li> 
        <li> 
            <label for="salary">Salary:</label> 
            <input type="text" name="salary" id="form_salary" value="" size="20"/> 
        </li> 
    </ol> 
    <input type="submit" name="submit" id="form_submit" value="Add Employee"/> 
</form>
}}
もう一度CSSスタイルシートを使用することでこのマークアップは
もう一度CSSスタイルシートを使用することで、上記のマークアップを装飾された見た目のFormに変換することができます。

プログラム化アプローチの[live demo|http://www.avoka.com/click-examples/form/contact-details.htm]を見ることが出来ます。

プログラム化アプローチの利点は冗長でないことです。それぞれのFieldはJavaを使って生成と追加が行われます。マークアップで再度Fieldを記述する必要はありません。

新しいフィールドの追加が要求された場合、Pageクラスのみ更新する必要があります。CSSとコンポーネントにより出力されるマークアップでレイアウトが解決されるためテンプレートを変更する必要はありません。

プログラム化アプローチの欠点は描画がコンテナによって行われるため、可視化が難しいことです。

[テンプレート|http://incubator.apache.org/click/docs/controls.html#template-layout]、または、[プログラム化|http://incubator.apache.org/click/docs/controls.html#programmatic-layout]レイアウトアプローチのどちらを使用するかはあなた次第です。それぞれ効果がありますし、お互いに対して利点と欠点を持っています。