Wicketで画面項目表示の分岐と反復

はじめに

 先日(といってもずいぶん前ですが)に、後輩とこんな会話がありました。

私 :Wicketもっとメジャーにならないかな。
   Web上に日本語の情報が増えれば安心感が出るかと思って、たびたびblogのネタにしてるのだけど。

後輩:えっ。id:sekomのblogがWicketの裾野を広げる役に立つと本当に思っているのですか?
   Wicketに対するネガティブな話題が多いと思うのですが……。

私 :……たしかに。

 しかし、ポジティブかつキャッチーなネタは思いつかなかったので、代わりに「Wicket始めたばかりの時、これが疑問だったなぁ……」というトピックの一部を今回は記録します。
 ちょっとは、Wicketの利用者の裾野を広げる役に立つ、はず。

対象読者

環境

何が疑問だったか

 WicketはHTMLをテンプレートにして画面を作るわけですが、そうすると以下の2点が気になるのです。

  • JSTLのc:if相当のことをどうやってやるのか
  • JSTLのc:forEach相当*1のことをどうやってやるのか

 しかし、直接的な説明がなかなか見つからず、理解が進むまで疑問のままでした。
 今ならば、「こう書けば、大体同じことが出来る」ということが分かるのでそれをメモします。

JSTLのc:if相当の実現方法

 Wicketに存在する以下の2点の機能を使います。

  これにより、JSTLのc:ifを使用したときにみたいに、「画面の一部分を条件によって差し替える」ということが実現できます。

 以下は現在時刻の末尾に応じて、画面表示の一部を差し替えるプログラムの例です。
 まずはHTML側の実装。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<body>
    <span wicket:id="message">絶対出るメッセージ</span><br />
	<span wicket:id="evenBlock">
	    <span wicket:id="evenMessage">偶数のときに絶対出るメッセージ</span><br />
	</span>
	<span wicket:id="oddBlock">
	    <span wicket:id="oddMessage">奇数のときに絶対出るメッセージ</span><br />
	</span>
</body>
</html>



 次にJava側の実装。

package sequenceSelectionIteration;

import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.PageLink;

public class SelectionPage extends WebPage {
	public SelectionPage() {
		super();
                add(new Label("message", "絶対出るメッセージ(分岐の例)"));
		WebMarkupContainer evenBlock = new WebMarkupContainer("evenBlock");
		add(evenBlock);
		evenBlock.add(new Label("evenMessage","偶数のときに絶対出るメッセージ"));
		evenBlock.add(new PageLink("evenLink",IterationPage.class));

		WebMarkupContainer oddBlock = new WebMarkupContainer("oddBlock");
		add(oddBlock);
		oddBlock.add(new Label("oddMessage","奇数のときに絶対出るメッセージ"));
		oddBlock.add(new PageLink("oddLink",IterationPage.class));

		//現在時間が偶数か奇数か判断して表示するブロックを切り替える。
		if(System.currentTimeMillis() % 2 == 1){
			evenBlock.setVisible(false);
			oddBlock.setVisible(true);
		}else{
			evenBlock.setVisible(true);
			oddBlock.setVisible(false);
		}
	}
}

実行時に出力されるHTMLの例です。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<body>
    <span wicket:id="message">絶対出るメッセージ(分岐の例)</span><br />
    <span wicket:id="evenBlock">
        <span wicket:id="evenMessage">偶数のときに絶対出るメッセージ</span><br />
        <a href="?wicket:interface=:1:evenBlock:evenLink::ILinkListener::" wicket:id="evenLink">偶数のときに出るリンク</a><br />
    </span>
</body>
</html>

 今回のように表示、非表示を切り替えたいコンポーネントが少ないならば、無理にWebMarkupContainerクラスを使わなくても良いのですが、例と言うことでその辺は許しておいてください。

 人によっては、あってもなくてもいいspanタグ(wicket:id="evenBlock"のspanタグ)が入ってしまうのが嫌かもしれませんけど、まあ、そこはご愛敬ということで。
 タグが入るのが嫌ならば、タグを消したいWebMarkupContainerクラスのインスタンスについて、setRenderBodyOnlyを引数trueで呼び出しておけば、タグを消すことも出来ます。

JSTLのc:forEach

 繰り返しは、ListViewクラスを使用します。
 ListViewクラスの使用感はforeachと割と似てます。
 簡単な説明を書きにくいので、以下に使用例のみ載せます。
 なんとなく使えてしまう程度の複雑さでも言いましょうか。

 Label要素の表示を繰り返す使用例です。
 まずはHTML側の実装。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<body>
繰り返しの例<br />
  <span wicket:id="listView">
    <span wicket:id="message">繰り返し</span>
  </span>
</body>
</html>



 次にJava側の実装。

ackage sequenceSelectionIteration;

import java.util.ArrayList;
import java.util.List;

import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;

public class IterationPage extends WebPage {

    public IterationPage() {
        super();
        List<String> list = new ArrayList<String>();
        list.add("メッセージ1");
        list.add("メッセージ2");

        add(new ListView("listView",list){
            private static final long serialVersionUID = 1L;

            protected void populateItem(ListItem item) {
                String message = (String)item.getModelObject();
                item.add(new Label("message", message));
            }
        });
    }
}



実行時に出力されるHTMLの例です。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<body>
繰り返しの例<br />
  <span wicket:id="listView">
  	<span wicket:id="message">メッセージ1</span>
  </span><span wicket:id="listView">
  	<span wicket:id="message">メッセージ2</span>
  </span>
</body>
</html>



出力されたHTMLのインデントが微妙に気持ち悪いですが、繰り返せています。

おわりに

 今回は自分の中で解決したときに、メモしてなかったことのメモでした。

 余談ですが、id:sekomは「c:if」と「c:forEach」をなぜ気にしてたのだろう、と思われる方もいるかもしれません。
 気にしていた理由は「1つの入り口と1つの出口を持つようなプログラムは、「順次・反復・分岐」の3つの基本的な論理構造によって記述できる」という構造化定理のためです。
 「順次・反復・分岐」が実現できるならば、概ねどんな画面レイアウトも作れるだろう、ということで最初の頃に特に関心を持っていたのでした。

*1:今見たら、Wicket in Actionの「1.2 Wicket in a nutshell」に言及がありました