Modelによる値の管理
フォームを扱う場合、理解しておかなければならない重要な点が2つあります。1つは「値の管理」について、もう1つは「送信のイベント処理」についてです。
まずは、値の管理について説明しましょう。Wicketでは、フォームのコントロール類もすべてコンポーネントとして用意されています。<input type="text">ならばTextField、<input type="Submit">ならばButtonというように対応するコンポーネントが用意されており、それを作成して先ほどの例と同じように組み込んでいきます。
ということは、コンポーネントの値を取り出したり変更したりすればいいんだな、と思ったかもしれません。しかし、これは実は間違いです。コンポーネントは、実はすべてのものが値を読み書きできるように設計されてはいません。例えば、Labelなどは、後から表示されているテキストを変更しようと思っても、それらしきメソッドは見つからないはずです。では、どうするのか?
Wicketのコンポーネントでは、値は「Model」と呼ばれるものによって管理されているのです。コンポーネントにModelを設定して作成することで、そのModelに用意された値が表示されるようになります。値を取り出したり変更したりしたければ、Modelを操作すればよいのです。このモデルは、org.apache.wicket.modelに用意されているModelクラスが一般に用いられます。
ここでは、入力フィールドと表示ラベルの値を管理する2つのModelインスタンスをprivateフィールドとして用意してあります。また、フォームの値を保管するための「CompoundPropertyModel」というものも用意しておきます。
private CompoundPropertyModel<WebPage> formmodel; private Model<String> fieldmodel; private Model<String> labelmodel;
Modelを使うときに注意したいのは「ModelはStringの値を管理するわけではない」ということです。Modelは、Objectとして値を管理します。ですから、基本的には、どんなオブジェクトであれ値として扱えることになります。しかし、実際問題として値の種類は統一されていなければきちんとModelは機能しません。そこで、このように汎用型を使い、Stringの値を指定しておくことにします。またフォームで使用されるCompoundPropertyModelは、WebPageインスタンスが保管されるので、ここではWebPageを汎用型として指定しておきます。
コンストラクタでは、用意したModelを利用してコンポーネントを作成しています。コンストラクタを見てみると、まずLabelを作成しています。1つ目は、そのまま表示するStringを引数に渡していますが、2つ目はModelを用意し設定しています。
labelmodel = new Model("何か書いて。");
Label label1 = new Label("label",labelmodel);
this.add(label1);
Modelは、引数にそのModelに用意する初期値を指定します。ここでは値としてStringを扱いますから、表示するテキストをそのまま指定しておきます。こうして作成したModelインスタンスをnew Labelする際に値として渡してやればいいのです。
続いて、フォーム関係のコンポーネント作成に進みます。まずは、フォームのコンポーネントです。これは、org.apache.wicket.markup.html.form.Formクラスとして用意されています。これも、値を管理するModelを用意します。コントロール類用のModelではなく、フォーム用に用意されているorg.apache.wicket.model.CompoundPropertyModelクラスを使用します。
formmodel = new CompoundPropertyModel<WebPage>(this);
Form<WebPage> form1 = new Form<WebPage>("form1",formmodel);
privateフィールドの宣言で、WebPageを値として保管するようにしてあるので、これも総称型を指定してインスタンスを作成します。引数にはModelの定義であるIModelインターフェースをimplementsしたオブジェクトが指定されます。これはthisを指定しておけばよいでしょう。
fieldmodel = new Model<String>();
TextField<String> field1 = new TextField<String>("field1",fieldmodel);
form1.add(field1);
続いて、入力フィールド用のTextFieldインスタンスの作成です。これもModelを用意して設定します。同じようにインスタンスを作成し、それからFormにaddをします。フォーム関係のコンポーネントは、このようにFormに対してaddし、最後にFormをthis.addして組み込みます。
Submitイベントの処理
続いて、SubmitボタンとなるButtonクラスのインスタンスを作成します。ButtonもModelインスタンスを渡しておきましたが、基本的に値をやり取りする必要はないので、第2引数は省略してもかまわないでしょう。
それよりも重要なのは、Buttonに割り当てるイベント処理です。Buttonクラスには、ボタンをクリックしてフォームを送信したときのイベント処理を行うために「onSubmit」というメソッドが用意されています。これをオーバーライドし、フォーム送信時の処理を用意しておくことができます。
Button submit = new Button("submit",new Model<String>()){
private static final long serialVersionUID = 1L;
@Override
public void onSubmit(){
submitNow();
}
};
ここでは、submitNowというメソッドを呼び出し、ここで具体的な処理をさせています。このsubmitNowメソッドで行っていることは、入力フィールドのModelから値を取り出し、LabelのModelに設定して表示を変更する作業です。
String s = fieldmodel.getObject();
labelmodel.setObject("あなたは、" + s + "と書いた。");
Modelから値を取得するには「getObject」メソッドを呼び出します。これは値をObjectインスタンスとして返しますが、ここでは総称型でStringを指定しているので、値は自動的にStringとして返されます。値の変更は「setObject」を使います。これでModelの値を変更することで、そのModelが設定されているコンポーネントの値が変わるわけです。
まとめ
Wicketのイベント処理を見て不安になった人はいないでしょうか。「これは、どこでフォームがGETされたりPOSTされた処理をしているんだ? クライアントからサーバに送信されて再びクライアントに返されるまでの流れがどうなっているのか、全然わからない」といった不安です。
Webアプリケーションの開発に慣れてしまった我々は、ついプログラムを「非常に原始的な手続き」として考えてしまいがちです。全体の手続きの流れを頭に入れ、クライアントからGETやPOSTでアクセスされるとまずこれが呼び出され、続いてこれが実行され・・・などとプログラムを考えていることに気が付くはずです。
Wicketが実現しようとしているのは、そうした「まったくオブジェクト指向らしくない開発スタイル」を排除し、コンポーネントによるオブジェクト指向らしいプログラミングスタイルを手に入れる、ということです。Wicketを使い始めた当初は、それまでの考え方にとらわれ、「リクエストはどこで取得するんだ? レスポンスの設定はどうするんだ?」といったことで悩んでしまう人も多いことでしょう。しかし、Wicketでは、そもそもリクエストやレスポンスといった考え方をしないのです。
最初のうちはかなり違和感を覚えるかもしれませんが、ある程度慣れてくると、「いかにもJavaらしいスタイル」でコードを作成できることが実感として分かるはずです。Wicketでは、「ネットワークのやり取り」という考え方を排除し、AWTなどと変わりのない「コンポーネントの定義」としてプログラムを構築できます。
サーバサイドの開発を「Javaの世界」に取り戻す。サーバ特有の決まりごとを取り除き、Java本来のプログラミングスタイルでWebアプリケーションを開発できるようにする。それがWicketの目的なのです。Wicketの利点が、なんとなくわかってきたでしょうか。
