Wicketでn行m列で折り返すリストを作る

[Java][Wicket]Wicketでn行m列で折り返すリストを作る

 Wicket本が未だに入荷しないので一つ書く。Wicket奮闘記第二弾。困り度的には、最悪1列表示で妥協すれば良かったのでそんなに高くない。

 ただのリストじゃなくて、要素が何個か横に続き、n個で折り返す……というサンプル。イメージとしては、mixiのマイミクとかマイコミュニティみたいな感じかなぁ。相変わらずバージョンは1.3なのです。

[f:id:kk_Ataka:20090316004745p:image]

 こんな感じ。横に2つで折り返し。

 どういう風に実現するかすごい困ったけれど、調べてみるとGridViewというコンポーネントがあったのでこれは使えそうだと。以下Javaソース。

//  テーブルのcolspanに設定する数
    private int column;
    private List<SampleObjectModelBean> selected;
    
    public SampleGridView() {
//      行の設定
        column = 2;
//      DBから何らかのリスト一覧を持ってきたという体で
        final List<SampleObjectModelBean> list = GenerateBeanUtility.GenerateSampleObjectModelBean();
//      自分が取得している項目という体で
        selected = new ArrayList<SampleObjectModelBean>();
        selected.add(list.get(2));
        selected.add(list.get(5));
        
//      DataProvider 規則的に回すもの
        IDataProvider dataProvider = new IDataProvider() {
            private static final long serialVersionUID = -9120134891423038532L;
            public Iterator<SampleObjectModelBean> iterator(int first, int count) {
                return list.iterator();
            }
            public int size() {
                return list.size();
            }
            public IModel model(Object object) {
                return new Model((Serializable)object);
            }
            public void detach() {
            }
        };
        
        final Form form = new Form("form");
        this.add(form);

        final CheckGroup checkGroup = new CheckGroup("checkGroup", new PropertyModel(this, "selected"));
        form.add(checkGroup);

        final WebMarkupContainer colspanWMC = new WebMarkupContainer("colspanWMC");
//      テーブルのcolspan属性を動的にしてやる
        colspanWMC.add(new SimpleAttributeModifier("colspan", String.valueOf(column)));
        checkGroup.add(colspanWMC);

        final GridView gridView = new GridView("rows", dataProvider) {
            private static final long serialVersionUID = 3658408852637870671L;
//          ListViewと同様のpopulateItem
            @Override
            protected void populateItem(Item item) {
                SampleObjectModelBean bean = (SampleObjectModelBean)item.getModelObject();
                item.add(new Check("check", item.getModel()));
                item.add(new Label("value", bean.getName()));
            }
//          空だった場合
            @Override
            protected void populateEmptyItem(Item item) {
                item.add(new Check("check").setVisible(false));
                item.add(new Label("value", "Empty"));
            }
        };
//      カラムの数をセット
        gridView.setColumns(column);
        checkGroup.add(gridView);

        final WebMarkupContainer listWMC = new WebMarkupContainer("listWMC");
        listWMC.setOutputMarkupId(true);
        this.add(listWMC);

//      選択したものを表示
        ListView listView = new ListView("resultList", selected) {
            private static final long serialVersionUID = 2797947925339607450L;
            @Override
            protected void populateItem(ListItem listItem) {
                SampleObjectModelBean bean = (SampleObjectModelBean)listItem.getModelObject();
                listItem.add(new Label("name", bean.getName()));
                listItem.add(new Label("outline", bean.getOutline()));
            }
        };
        listWMC.add(listView);

//      選択決定ボタン
        AjaxButton button = new AjaxButton("button") {
            private static final long serialVersionUID = 7635585733674755967L;
            @Override
            protected void onSubmit(AjaxRequestTarget target, Form form) {
                target.addComponent(listWMC);
            }
        };
        form.add(button);
        
//      列を増やしてみる
        AjaxLink incLink = new AjaxLink("incLink") {
            private static final long serialVersionUID = 7734634038402851693L;
            @Override
            public void onClick(AjaxRequestTarget target) {
                if (column < list.size()) {
                    column++;
                }
                colspanWMC.add(new SimpleAttributeModifier("colspan", String.valueOf(column)));
                gridView.setColumns(column);
                target.addComponent(form);
            }
        };
        this.add(incLink);
        
//      列を減らしてみる
        AjaxLink decLink = new AjaxLink("decLink") {
            private static final long serialVersionUID = -3611671121498659140L;
            @Override
            public void onClick(AjaxRequestTarget target) {
                if (column > 1) {
                    column--;
                }
                colspanWMC.add(new SimpleAttributeModifier("colspan", String.valueOf(column)));
                gridView.setColumns(column);
                target.addComponent(form);
            }
        };
        this.add(decLink);
    }
    
    public List<SampleObjectModelBean> getSelected() {
        return selected;
    }
    public void setSelected(List<SampleObjectModelBean> selected) {
        this.selected = selected;
    }

 で、html。

    <h1>GridViewを用いてテーブルをn列で折り返した選択フォーム(例:mixiのコミュニティ一覧みたいな)</h1>
    <form wicket:id="form">
        <input type="submit" wicket:id="button" />
        <table cellspacing="0" cellpadding="2" border="1">
            <span wicket:id="checkGroup">
                <tr>
                    <th wicket:id="colspanWMC">なんとか一覧</th>
                </tr>
                <tr wicket:id="rows">
                    <td wicket:id="cols">
                        <input type="checkbox" wicket:id="check" />
                        <span wicket:id="value" />
                    </td>
                </tr>
            </span>
        </table>
    </form>
    <h1>(おまけ)列を動的に変えてみる</h1>
    <ul>
        <li><a href="#" wicket:id="decLink">列減らす</a></li>
        <li><a href="#" wicket:id="incLink">列増やす</a></li>
    </ul>
    <h1>選択したもの</h1>
    <span wicket:id="listWMC">
        <dl wicket:id="resultList">
            <dt wicket:id="name"></dt>
            <dd class="recital" wicket:id="outline"></dd>
        </dl>
    </span>

 列を変えるボタンのソースとかはおいといて……、とりあえず

[f:id:kk_Ataka:20090316004745p:image]

 の画面は実現できます。普通のListViewでコレクションを表示していくのと違う点は、

  1. 1. ListViewがGridView - -表示するオブジェクトがModel(かList)ではなく、DataProvider - populateEmptyItemの処理 - カラム数をセット
    1. wicket:idでちょこっと

 このくらいです。

 まず、ListViewがGridView。これはそういうコンポーネントがあるのかぁ的な感じでゴリゴリ変えていきます。

 次に、GridViewに変えた事で引数にModelとかListが使えなくなりました……。代わりにDataProviderというものを突っ込んでやらないといけないらしい……。DataProviderはこんな感じ。listはDBから取得してきたという体で生成した選択項目全部のリストです。

        IDataProvider dataProvider = new IDataProvider() {
            private static final long serialVersionUID = -9120134891423038532L;
            public Iterator<SampleObjectModelBean> iterator(int first, int count) {
                return list.iterator();
            }
            public int size() {
                return list.size();
            }
            public IModel model(Object object) {
                return new Model((Serializable)object);
            }
            public void detach() {
            }
        };

 とりあえずこんな感じで動きました。iteratorメソッドには引数が二つあったりするんですが、次回は使います。今回は使わなくても動いた……。[1]

 これで、回すのに必要なものは揃ったので、populateItemをオーバライドしますが、populateItemとは別にpopulateEmptyItemというメソッドもオーバライドしないといけません。理由は後述。

 次にテーブルを何列で折り返したいかをsetColumnsメソッドで指定します。今回は頭の方で2に設定しているので、n行2列のテーブルに出来るはずです。しかし、列を指定した事でテーブルに空白が出来る可能性が起こってしまいます。2列のテーブルなら、リストが奇数の場合最後の行が1列しか埋まりません;

 ここでpopulateEmptyItemの出番です。テーブルがまだ全部埋まっていないのに要素が終わってしまった場合、これが呼ばれます。

[f:id:kk_Ataka:20090316004745p:image]

 Emptyと表示しているところがpopulateEmptyItemが呼ばれている箇所です。めでたしめでたし。

 最後にwicket:id。

        final GridView gridView = new GridView("rows", dataProvider) {
            private static final long serialVersionUID = 3658408852637870671L;
//          ListViewと同様のpopulateItem
            @Override
            protected void populateItem(Item item) {
                SampleObjectModelBean bean = (SampleObjectModelBean)item.getModelObject();
                item.add(new Check("check", item.getModel()));
                item.add(new Label("value", bean.getName()));
            以下略

 JavaでWicket:idを生成しているのは、

  • - GridViewのrows - -populateItem内のCheckのcheck - populateItem内のLabelのvalue

 の三つなんですが、この三つをListView的な感じでバインドしていっても、

WicketMessage: Unable to find component with id 'check' in [MarkupContainer [Component id = 1, page = sampleWicket.view.gridView.SampleGridView, path = 2:form:checkGroup:rows:1.Item, isVisible = true, isVersioned = false]]. This means that you declared wicket:id=check in your markup, but that you either did not add the component to your page at all, or that the hierarchy does not match.

 checkがない! と怒られてしまいます。

 なんで>< と調べてみると、どうも、

                <tr wicket:id="rows">
                    <td wicket:id="cols">
                        <input type="checkbox" wicket:id="check" />
                        <span wicket:id="value" />
                    </td>
                </tr>

 rows(=GridView)の下にcolsというidを振らないといけないらしい。GridViewクラスの中にも

        Item rowItem = newRowItem(newChildId(), row);
        RepeatingView rowView = new RepeatingView("cols");
        rowItem.add(rowView);
        add(rowItem);

 こんな記述があって、どうもcolsを振っているらしい! 振れば動く!

 最後はらしいらしいが続いてますが、一応これでmixiのマイピクみたいなテーブルも作れそうです。

 この記事を書くに当たって、[http://hamachan.info/excel/gyoretu.html:title]で行と列のわかりやすい覚え方を知りました

 行と列わからないんです>< でもこれで忘れない!

cheat

某研究室のcheatです
なんかエラーググってたらみつけますた

1247234728

ikura

スタイルシートつかって要素のサイズを決めて、float:leftで設置していくのでは駄目なんでしょうか??

1320027787

kk_Ataka

2年前の事なので定かではないですが、この時はWicketでなんとかできないかと考えた末の方法だったのだと思います!

1321447309

[1] 不都合が起こるパターンもあるのだろうか?