はじめに
Apache Wicket 7.0.0 のリリースが目前に迫っています。楽しみですね。
ところで、以前のマイルストーンリリースの時に、
@gishi_yama 14章のComponent queueingが新しく追加されたんですか。コンポーネントのバインドミスが減るみたいで嬉しいんですがどうなんでしょうね
— きゃべ (@kyabe) 2015, 5月 19
という話があったのを思い出したので、Component queueingを試してみました。
結論から言うと、wicket:idが被らなければとても良い感じです。
Component#queue(Component)
Formをもつ、以下の様なHTMLをWicketで制御したいとします。
<body>
<div>
<div wicket:id="feedback"></div>
<form wicket:id="form">
名前:<input type="text" wicket:id="name" /><br />
番号:<input type="text" wicket:id="index"/><br />
<input type="submit" />
</form>
<h1 wicket:id="result"></h1>
</div>
</body>
このページを Wicket 6 までの作り方で構築する場合、HomePage.javaでは
Form<Void> form = new Form<Void>("form") {
// -- snip --
}
add(form);
form.add(new TextField<String>("name", nameModel));
form.add(new TextField<String>("index", nameModel));
add(new Label("result", resultModel));
といった様に、<form>
と<input>
(Form
とTextField
)の階層構造、つまりHTML上のwicket:idの階層構造をもとに、Javaコード上でも同じ階層構造となるように意識して親子のコンポーネントをaddする必要がありました。
Wicket 7 から採用されるComponent queueingでは、wicket:idが重複しない限り、
queue(new Form<Void>("form") {
// -- snip --
});
queue(new TextField<String>("name", nameModel));
queue(new TextField<String>("index", nameModel));
queue(new Label("result", resultModel));
といった様に、wicket:idの階層を意識せずにコンポーネントを準備できるようになります。
よりすっきりとしたコードになって気持ちいいですね。
wicket:idが重複したら?
ページ内でwicket:idが重複してしまった場合は、従来通りwicket:idの階層構造にもとづいてqueueメソッドを使います。
たとえば、上記のHomePage.htmlの"result"
が"name"
というwicket:idだったとしたら、
Form<Void> form = new Form<Void>("form") {
// -- snip --
});
queue(form);
form.queue(new TextField<String>("name", nameModel));
form.queue(new TextField<String>("index", nameModel));
queue(new Label("name", resultModel));
というように、従来と同じ書き方(addがqueueに変わっただけ)になってしまいます。
ここだけを注視すると「addでいいんじゃね?」という声もわき上がってきそうですが...(注1)
一部を除いてJavaコードを書くときにHTML(wicket:id)の階層を意識しなくてもよいという開放感が、現状のqueueの嬉しい所ではないでしょうか。
おわりに
個人的にはWicketのテストケースの様に
queue(new TextField("form:name", nameModel));
みたいに書けるとより気持ちいいなーと思いますが、残念ながらこれはできません(注2)。そのため、全てのコンポーネントをフラットにqueueで追加するためには、たとえば、アンダースコアつなぎのユニークなwicket:idを考えるとかいう前提の元になっちゃいそうです(それはそれで悪手な気が...)。
なので、Wicket 7 を採用する場合は、
- add原理主義
- queueを受け入れ、原則はqueueで、どうしても重複する場合は階層構造前提で
というどちらかのやり方で統一するのが妥当かなと思います。
最後に、上のHomePage.htmlに対してWicket7で作ってみたHomePage.javaの内容を記載しておきます(動作確認優先で書いていますので、Modelの使い方などはご容赦ください)。
public class HomePage extends WebPage {
public HomePage() {
IModel<String> nameModel = Model.of("");
IModel<Integer> indexModel = Model.of(1);
IModel<String> resultModel = Model.of("");
queue(new FeedbackPanel("feedback"));
queue(new StatelessForm<Void>("form") {
@Override
protected void onSubmit() {
super.onSubmit();
List<String> omikujis = Stream.of("大吉", "中吉", "小吉", "吉", "凶")
.collect(Collectors.toList());
Collections.shuffle(omikujis);
String result = nameModel.getObject()
+ "さんの運勢は..."
+ omikujis.get(indexModel.getObject() - 1);
resultModel.setObject(result);
}
});
queue(new RequiredTextField<String>("name", nameModel) {
@Override
protected void onInitialize() {
super.onInitialize();
add(StringValidator.lengthBetween(1, 10));
add(new HTML5Attributes());
setLabel(Model.of("名前"));
}
});
queue(new RequiredTextField<Integer>("index", indexModel, Integer.class) {
@Override
protected void onInitialize() {
super.onInitialize();
add(RangeValidator.range(1, 5));
add(new HTML5Attributes());
setLabel(Model.of("番号"));
}
});
queue(new Label("result", resultModel) {
@Override
protected void onConfigure() {
super.onConfigure();
setVisible(!Objects.equals(getDefaultModelObjectAsString(), ""));
}
});
}
}
ぜひ皆さんも Wicket 7 で楽しいコンポーネントキューイング生活を送ってみませんか。
- 注1)初めてユーザーガイドを読んだ時は、私も「addでいいんじゃね...」って思いました。
- 注2)wicket:idでは
:
が禁則文字として扱われていたりします。form:nameの様に階層構造を指定できるのは、Wicketが自動的に生成するときか、テストケースの時だけのようです。