Wicket In Action Chapter9読了


またまた仕事の都合で先にChapter9のカスタムコンポーネントを読むことにした。

画面部品の再利用を効果的に行うにあたってのノウハウが満載のこの章ですが、特に勉強になったのは FormComponentPanel でした。
このクラスは Panel に似た使い方ができるのですが、使う側から見たら TextField の様な一つのコンポーネントだけど、内部的には複数のコンポーネントで構成されている Formコンポーネントを作成するのに使用します。
例として掲載されているコードは、外部から見たら Date型の値をModelとするコンポーネントだけど、内部的には 年月日、時間、分のTextFieldを持っているカスタムコンポーネントです。
onBeforeRenderメソッドで与えられたModelの値を内部の各コンポーネントに振り分け、convertInputメソッドで外部に渡すModelの値を構成するようにします。

試しに作ってみたのは、内部的には 姓・名 をもつコンポーネント。
外部から見ると姓・名 を空白で区切った値をやりとりするんだけど、内部的には別々のフィールドとして定義されています。
 import org.apache.wicket.markup.html.form.FormComponentPanel;
 import org.apache.wicket.markup.html.form.TextField;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.PropertyModel;

 public class CustomForm extends FormComponentPanel {
 	private String firstName;
 	private String lastName;
 	private String name;
 	private TextField firstNameField;
 	private TextField lastNameField;

 	public CustomForm(String id) {
 		this(id, null);
 	}
 	public CustomForm(String id, IModel model) {
 		super(id, model);

 		add(firstNameField = new TextField("first", new PropertyModel(this, "firstName"), String.class));
 		add(lastNameField = new TextField("last", new PropertyModel(this, "lastName"), String.class));
 	}
 	@Override
 	protected void onBeforeRender() {
 		name = (String) getModelObject();
 		if (name != null) {
 			if (name.split("\\s").length == 2 ) {
 				lastName = name.split("\\s")[1];
 				firstName = name.split("\\s")[0];
 			}
 		}
 		firstNameField.setRequired(isRequired());
 		lastNameField.setRequired(isRequired());
 		super.onBeforeRender();
 	}

 	@Override
 	protected void convertInput() {
 		setConvertedInput(firstNameField.getInput() + " " + lastNameField.getInput());
 	}
 }
使う側としては、下記のようにして一つのコンポーネントとして使用します。
form.add(new CustomForm("name", new Model("Hoge Fuga")));
onBeforeRenderメソッドで"Hoge Fuga"がCustomFormクラス内部のfirstName, lastNameフィールドにセットされます。
Submitされた時は、convertInputメソッドでfirstNameFieldの値とlastNameFieldの値が空白で区切って結合され、外部からはこの値が見えます。

この辺の画面部品の作りやすさはさすが Wicket って感じです。


Wicket in Action Table of Contents
Part 1 Getting started with Wicket
1. What is Wicket?
2. The architecture of Wicket
3. Setting up a Wicket project
4. Building a cheesy Wicket application

Part 2 Getting a basic grip on Wicket
5. Understanding models
6. Using basic components
7. Using forms for data entry
8. Composing your pages

Part 3 Advanced Wicket
9. Creating custom components
10. Working with Wicket resources
11. Rich components and Ajax
12. Authentication and authorization
13. Localization
14. Multi-tiered architecture
15. Putting your application in production
16. Component index

Comment

  1. Saxman
    2008-06-23 Mon 04:35

    >例として掲載されているコードは、外部から見たら Date型の値をModelとするコンポーネントだけど、内部的には 年月日、時間、分のTextFieldを持っているカスタムコンポーネントです。
    こんなコンポーネントは結構役に立ちそうですね。
    DB上は文字列で保持しており、実際には日付チェックなどのためDate型として扱いたい場合や、その逆もあると思いますし。

    >この辺の画面部品の作りやすさはさすが Wicket って感じです。
    でも、やっぱり内部的な動きを知らないと気持ち悪いですね・・・。
    tmaさんの例で言うと、コンストラクタでIModelを指定している部分とonBeforeRender()メソッドでgetModelObject()メソッドで取得するmodelが同一であるかどうか(実際にはComponentクラスで保持されそれが取得できるので同一であるが一見分からない)
    また同様に、
    add(firstNameField = new TextField("first", new PropertyModel(this, "firstName"), String.class));
    で指定されるPropertyModelについてもTextFieldでどう振舞うのかが分かりづらいんですよね。書き方のルール的なものを覚えたら慣れるのかもしれませんが、もう少し分かりやすければなぁ。と思います。

    ちなみに私の理解はMFCでのドキュメントビューアーキテクチャのようにView生成時にDocument側のクラスインスタンスを渡し(this渡しする)相互関係を持つことでViewとDocument(Model)の相互関係を持つ。と同様なのですが、Wicketの場合はそこにリクエスト処理の部分が紐付くことと、自分でそれらを記述することが必要であると言うことを別途理解しておく必要があるように感じます。
    (VisualC++のクラスウィーザードの便利さを改めて実感・・・)

  2. tma
    2008-06-23 Mon 06:23

    今回Wicket In Actionに掲載されているコンポーネントはかなり実用的ですね。

    確かにModelまわりは私もブラックボックスな部分が多いです。もうちょっとソースを追って見たいと思ってます。なんか他にも便利機能があるかもしれないし、逆に落とし穴があるかもしれないですし。

    PropertyModelは簡潔には書きやすいんで使っちゃうことがあるんですけど、おっしゃる通り かなりWicket Wayなんで、実際にWebアプリを組むときは意識的に使わないようにしてたりします。