Wicketでn行m列で折り返すリストを作る
Wicket本が未だに入荷しないので一つ書く。Wicket奮闘記第二弾。困り度的には、最悪1列表示で妥協すれば良かったのでそんなに高くない。
ただのリストじゃなくて、要素が何個か横に続き、n個で折り返す……というサンプル。イメージとしては、mixiのマイミクとかマイコミュニティみたいな感じかなぁ。相変わらずバージョンは1.3なのです。
こんな感じ。横に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>
列を変えるボタンのソースとかはおいといて……、とりあえず
の画面は実現できます。普通のListViewでコレクションを表示していくのと違う点は、
-
- ListViewがGridView
-
- 表示するオブジェクトがModel(かList)ではなく、DataProvider
- populateEmptyItemの処理
- カラム数をセット
-
- wicket:idでちょこっと
- ListViewがGridView
このくらいです。
まず、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の出番です。テーブルがまだ全部埋まっていないのに要素が終わってしまった場合、これが呼ばれます。
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())); 以下略
-
- GridViewのrows
-
- populateItem内のCheckのcheck
- populateItem内のLabelのvalue
-
- GridViewのrows
の三つなんですが、この三つを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のマイピクみたいなテーブルも作れそうです。
この記事を書くに当たって、行と列の覚え方で行と列のわかりやすい覚え方を知りました。
行と列わからないんです>< でもこれで忘れない!
*1:不都合が起こるパターンもあるのだろうか?