wicket: Ajaxボタンで動的に値を追加するテーブルを作る

#はじめに
Ajaxボタンをクリックすることでテーブル内の要素の値が増えるページを作りたい。
ただし、決まった数増えたら一段下に増えるように。

#完成予想図
![イメージ図.jpeg](https://qiita-image-store.s3.amazonaws.com/0/57201/9563409c-3947-6d22-a39c-c617abec4f91.jpeg)

#完成画面　キャプチャ
http://cl.ly/image/3h2u0L3L1O1w

#過程（1） 2014/12/19
モックアップのhtmlと空のjavaを作成

```Page.java
package page;

import org.apache.wicket.markup.html.WebPage;

/**
 * ボタンで動的に要素の値を追加するテーブルを作る
 * 
 * @author IS masaki okabe
 * 
 */
public class Page extends WebPage {

	private static final long serialVersionUID = -2093104879611846359L;

	public Page() {
	}

}
```

```Page.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"
	xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd"
	lang="ja" xml:lang="ja">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
	<div style="width: 800px; margin: 100px">
		<h2>ajaxボタンで動的に変化するtableを作りたい</h2>
		<h2>前</h2>
		<table border="1px">
			<thead>
				<tr>
					<th style="width: 130px; text-align: center">カラム1</th>
					<th style="width: 130px; text-align: center">カラム2</th>
					<th style="width: 130px; text-align: center">カラム3</th>
					<th style="width: 130px; text-align: center">カラム4</th>
					<th style="width: 130px; text-align: center">カラム5</th>
					<th style="width: 130px; text-align: center">カラム6</th>
					<th style="width: 130px; text-align: center">カラム7</th>
					<th style="width: 50px; text-align: center"></th>
				</tr>
			</thead>
			<tbody>
				<tr align="center">
					<td rowspan="4">A</td>
					<td>B</td>
					<td>

						<div>
							<a href="#">1</a>
						</div>
					</td>
					<td>
						<div>
							<a href="#">2</a>
						</div>
					</td>
					<td>
						<div>
							<a href="#">3</a>
						</div>
					</td>
					<td>
						<div></div>
					</td>
					<td>
						<div></div>
					</td>
					<td>

						<div>
							<a href="#"> <span> + </span>
							</a>
						</div>
					</td>
				</tr>
			</tbody>
		</table>

		
	</div>
</body>
</html>
```

テーブル内の繰り返し部分（カラム3〜7）をListViewに

```Page.java
~略~

	public Page() {
		
		//テーブルにデフォルトセットされているデータを用意
		List<String> tdDatas = new ArrayList<String>();
		tdDatas.add("1番目");
		tdDatas.add("2番目");
		tdDatas.add("3番目");
		tdDatas.add("4番目");
		tdDatas.add(""); //　※投稿本文で説明
		
		//tdDatasを保持するModelを用意
		IModel<List<String>> tdListViewModel = new ListModel<String>(tdDatas);
		
		//テーブルのカラム3〜7の値を決めるListView
		ListView<String> tdListView = new ListView<String>("tdListView",tdListViewModel) {

			private static final long serialVersionUID = -5282734444412568671L;

			@Override
			protected void populateItem(ListItem<String> item) {
				Link<Void> data = new Link<Void>("data"){
					
					private static final long serialVersionUID = 1301603993310363062L;

					@Override
					public void onClick() {
					}
				}; 
				data.add(new Label("dataLabel", item.getModelObject()));
				
				item.add(data);
				
			}
		};
		
		this.add(tdListView);
	}

}
```

```Page.html
~略~
			<tbody>
				<tr align="center">
					<td rowspan="4">A</td>
					<td>B</td>
					<td wicket:id="tdListView">
						<div>
							<a href="#" wicket:id="data"><span wicket:id="dataLabel">ここにデータラベルが入る</span></a>
						</div>
					</td>
					
					<td>
						<div>
							<a href="#"> <span> + </span></a>
						</div>
					</td>
				</tr>
			</tbody>
~略~
```

※Page.java内で空文字をtdDatasnにaddしているのは、
ListViewで吐き出される\<td>要素の数がカラム数と合っていない場合、表示がおかしくなるから。
こうしたいのに↓
![スクリーンショット 2014-12-19 20.04.37.jpg](https://qiita-image-store.s3.amazonaws.com/0/57201/10ad89ec-3c2a-33e2-9956-c36d4b7e1676.jpeg)

こうなる↓
![スクリーンショット 2014-12-19 20.04.00.jpg](https://qiita-image-store.s3.amazonaws.com/0/57201/84f5d9dc-d425-eb40-1220-7a908d8ac0ba.jpeg)

#過程（2） 2015/01/05
複数列になった場合でも繰り返したいカラム（カラム3〜7）と、
そうでないカラム（カラム1、2、＋ボタン）の順番が崩れないようなListViewを作成

```Page.java
~略~
	
	public Page() {
		
		List<List<String>> datas = new ArrayList<List<String>>();
		
		//テーブルにデフォルトセットされているデータを用意
		List<String> tdDatas = new ArrayList<String>();
		tdDatas.add("1番目");
		tdDatas.add("2番目");
		tdDatas.add("3番目");
		tdDatas.add("4番目");
		tdDatas.add("5番目"); 
		
		//2列目のデータ
		List<String> tdDatas2 = new ArrayList<String>();
		tdDatas2.add("2列目1番目");
		tdDatas2.add("");
		tdDatas2.add("");
		tdDatas2.add("");
		tdDatas2.add(""); 
		
		datas.add(tdDatas);
		datas.add(tdDatas2);
		
		//列を制御するリストビュー
		IModel<List<List<String>>> rowListViewModel = new ListModel<List<String>>(datas);
		
		//テーブルのカラム3〜7の値を決めるListView
		ListView<List<String>> rowListView = new ListView<List<String>>("rowListView",rowListViewModel) {

			private static final long serialVersionUID = -5282734444412568671L;

			@Override
			protected void populateItem(ListItem<List<String>> item) {

				//カラム1＆2のラベル
				Label column1 = new Label("column_1","A");
				Label column2 = new Label("column_2","B");

				//＋ボタンのリンク
				Link<Void> addDataButton = new Link<Void>("addDataButton"){

					private static final long serialVersionUID = 1301603993310363062L;

					@Override
					public void onClick() {
						System.out.println("click");
					}
					
				};
				
				//2列目以降は上記コンポーネントを非表示に
				if(item.getIndex() > 0){
					column1.setVisible(false);
					column2.setVisible(false);
					addDataButton.setVisible(false);
				}
				
				//カラムのデータを保持するModelを用意
				IModel<List<String>> columnListViewModel = new ListModel<String>(item.getModelObject());
				
				//カラムを表示するリストビュー
				ListView<String> columnListView = new ListView<String>("columnListView",columnListViewModel) {

					private static final long serialVersionUID = -5282734444412568671L;

					@Override
					protected void populateItem(ListItem<String> item) {
						Link<Void> data = new Link<Void>("data"){
							
							private static final long serialVersionUID = 1301603993310363062L;

							@Override
							public void onClick() {
							}
						}; 
						data.add(new Label("dataLabel", item.getModelObject()));
						
						item.add(data);
						
					}
				};

				item.add(columnListView);
				item.add(column1,column2,addDataButton);
			}
			
		};
		
		this.add(rowListView);
	}

~略~
```

現在の画面
![スクリーンショット 2015-01-05 12.02.59.jpg](https://qiita-image-store.s3.amazonaws.com/0/57201/052c286b-592a-b7b8-3cc7-966c06c69552.jpeg)
wicketのコンポーネントを使って正しい形の表を出力できた。
まだ動的にカラムデータが変化しない。

#最終過程（3） 2015/01/05-2
ボタンを押すことで空のカラムへ値が増えるように。空のカラムがない場合は一行増やす。
動作キャプチャ
http://cl.ly/image/3h2u0L3L1O1w

```Page.java
~略~
	public Page() {
		
		//tbody用WMC
		final WebMarkupContainer tbodyWMC = new WebMarkupContainer("tbodyWMC"){
			
			private static final long serialVersionUID = -2217415656725425892L;

			@Override
			protected void onInitialize() {
				super.onInitialize();
				setOutputMarkupId(true);
			}
		};
		this.add(tbodyWMC);
		
		List<List<String>> datas = new ArrayList<List<String>>();
		
		//テーブルにデフォルトセットされているデータを用意
		List<String> tdDatas = Arrays.asList("1番目","2番目","3番目","4番目","5番目");
		
		//2列目のデータ
		List<String> tdDatas2 = Arrays.asList("2列目1番目","2列目2番目","","",""); 
		
		datas.add(tdDatas);
		datas.add(tdDatas2);
		
		//列を制御するリストビュー
		final IModel<List<List<String>>> rowListViewModel = new ListModel<List<String>>(datas);
		
		//テーブルのカラム3〜7の値を決めるListView
		ListView<List<String>> rowListView = new ListView<List<String>>("rowListView",rowListViewModel) {

			private static final long serialVersionUID = -5282734444412568671L;

			@Override
			protected void populateItem(final ListItem<List<String>> item) {

				//カラム1＆2のラベル
				Label column1 = new Label("column_1","A");
				Label column2 = new Label("column_2","B");

				//＋ボタンのリンク
				AjaxLink<Void> addDataButton = new AjaxLink<Void>("addDataButton"){

					private static final long serialVersionUID = 1301603993310363062L;

					//リストの末尾に新しい値を一つ追加し、tbodyを更新する
					@Override
					public void onClick(AjaxRequestTarget target) {
						
						addNewValue(rowListViewModel.getObject());
						target.add(tbodyWMC);
						
					}
					
				};
				
				//2列目以降は上記コンポーネントを非表示に
				if(item.getIndex() > 0){
					column1.setVisible(false);
					column2.setVisible(false);
					addDataButton.setVisible(false);
				}
				
				//カラムのデータを保持するModelを用意
				IModel<List<String>> columnListViewModel = new ListModel<String>(item.getModelObject());
				
				//カラムを表示するリストビュー
				ListView<String> columnListView = new ListView<String>("columnListView",columnListViewModel) {

					private static final long serialVersionUID = -5282734444412568671L;

					@Override
					protected void populateItem(ListItem<String> item) {
						Link<Void> data = new Link<Void>("data"){
							
							private static final long serialVersionUID = 1301603993310363062L;

							@Override
							public void onClick() {
							}
						}; 
						data.add(new Label("dataLabel", item.getModelObject()));
						
						item.add(data);
						
					}
				};

				item.add(columnListView);
				item.add(column1,column2,addDataButton);
			}
			
		};
		
		tbodyWMC.add(rowListView);
	}
	
	//横幅5個、縦幅不定数の2次元リストの横軸の末尾に新しい値を追加する。
	//横幅の端に到達したら縦幅を一段伸ばして、新しい値を追加する。
	public void addNewValue(List<List<String>> listList){
		
		boolean setFlag = true;
		
		for(List<String> list : listList){
			for(int i = 0; i < list.size(); i++){
				if(list.get(i).isEmpty()){
					list.set(i, "新しい値");
					setFlag = false;
					break;
				}
			}
		}
		
		if(setFlag){
			List<String> list = Arrays.asList("新しい値","","","","");
			listList.add(list);
		}
		
		
	}
~略~
```

```Page.html

			<tbody wicket:id="tbodyWMC">
				<wicket:container wicket:id="rowListView">
					
				<tr align="center">
					<td rowspan="99" wicket:id="column_1"></td>
					<td rowspan="99" wicket:id="column_2"></td>
					
					<td wicket:id="columnListView">
						<div>
							<a href="#" wicket:id="data"><span wicket:id="dataLabel">ここにデータラベルが入る</span></a>
						</div>
					</td>
					
					<wicket:enclosure>					
					<td rowspan="99" wicket:id="addDataButton">
						<span> ＋ </span>
					</td>
					</wicket:enclosure>
				</tr>
				
				</wicket:container>
			</tbody>
```
#まとめ
2重リストをListViewで回して値を出力し、
値の追加時に画面更新を行った。
値が動的に変化しないカラム（カラム1、2、＋ボタン）は、
2行目以降表示されないように表示フラグをoffに変更。

#終わりに
作業ステップがわかるように書いたことが、いつか身を助けますように。

Qiitaはgif動画の埋め込みができなくて残念。

コードの行数を表示する方法はあるのかな。