Springboot + HandsOnTableで一覧編集ができるフォームを作成する。

##SpringBoot+HandsOnTableで、テーブルの一覧をExcel風に編集できる画面を作成する

従業員マスタを編集するイメージです。

###1.従業員マスタ

```Employee.java
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
@Data
public class Employee implements Serializable {
	//選択の有無
	private boolean selected;
	//従業員番号
	private long empNo;
	//従業員氏名
	private String empName;
	//役職
	private String position;
	//採用日
	@JsonFormat(pattern = "yyyy/MM/dd")
	private String hireDate;
	public Employee() {	}
	public Employee(long empNo, String empName, String position, String hireDate) {
		this.empNo=empNo;
		this.empName=empName;
		this.position=position;
		this.hireDate=hireDate;
	}
}
```

```EmployeeListForm.java
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import lombok.Data;
@Data
public class EmployeeListForm implements Serializable {
	List<Employee> list=new ArrayList<Employee>();
}
```

###2.Controller
####(1) 初期表示用

```HeloController.java
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HeloController {
	@RequestMapping("/")
	public String top() {
		return "index";
	}
	@RequestMapping(value="/regist", method=RequestMethod.POST)
	public String regist(
		@ModelAttribute EmployeeListForm form,
		Model model) {
		model.addAttribute("form", form);
		for (Employee emp: form.getList()) {
			System.out.println(emp.toString());
		}
		return "index";
	}
}
```

####(2) HandsOnTableのData

HandsOnTableにマッピングする従業員マスタの一覧を取得するメソッド。

```HeloRestController.java
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HeloRestController {
    @RequestMapping("/getEmpoyeeList")
    public List<Employee> getEmpoyeeList() {
    	DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
    	List<Employee> list = new ArrayList<>();
    	for (int i=0; i< 1000; i++) {
    		list.add(
    			new Employee((i+1),
    			"name-"+String.valueOf((i+1)),
    			String.valueOf(i%4 + 1),
    			LocalDate.now().format(dateTimeFormatter)));
    	}
    	return list;
    }
}
```


###3.html

```index.html
<!DOCTYPE html>
<html lang="ja" xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link th:href="@{/handsontable/handsontable.full.min.css}" rel="stylesheet" />
<script th:src="@{/webjars/jquery/3.3.1/jquery.min.js}"></script>
</head>
<body>
    <input type="hidden" id="posturl" th:value="@{/getEmpoyeeList}"/>
    <form id="postform" th:action="@{/regist}" method="post">
    </form>
    <H3>Hands on Table サンプル</H3>
    <p>行を追加するには、右クリックして「上に行を挿入」を選択します<p>
    <div style="height:600px;width:1000px;padding:10px;">
	    <div id="grid" class="handsontable"></div>
    </div>
    <button type="button" id="send">送信</button>
    <br>
    <script th:src="@{handsontable/handsontable.full.min.js}"></script>
	<script th:inline="javascript">
	/*<![CDATA[*/

    /** HandsOnTableのコンテナ  */
	var container = document.getElementById('grid');  //後ほど表を展開する要素を指定
    /** HandsOnTableオブジェクト  */
    var htable;
    /** HandsOnTableのヘッダ  */
    var header;
    /** HandsOnTableのData  */
    var jsondata;

    /** ajaxを実行する  */
	function executeAjax(ajaxUrl, ajaxType, formData){
      return $.ajax({
        url: ajaxUrl,
        type: ajaxType,
        contentType:false,
        processData: false,
        cache: false,
        data: formData,
        timeout: 30000
      });
    }

	//HandsOnTableオブジェクト作成
    function createHandsOnTable(jsonData){

		jsondata = jsonData;
    	//ヘッダを編集
	    header = new Array();
	    var count = 0;
	    for(key in jsondata[0]){
	      header[count] = key;
	      count++;
	    }
		htable = new Handsontable(container, {
		  //以下はデータ指定と表示オプション
		  data: jsondata,        //JsonData
//		  minSpareRows: 1,       //表の一番下にいくつ空行を表示するか。今回は１行を空行にして表示する。
		  colHeaders: header,    //カラムの名前を表示するかどうか colheader: true 表示/false 非表示
		                         //カラムの名前を任意の名前にする colheader: 配列（カラム名）
		  columns: [
			//選択
            {data:"selected",type: 'checkbox'},
			//従業員番号
            {data:"empNo",type: 'numeric'},
			//従業員氏名
            {data:"empName",type: 'text'},
			//採用日は、autocomplete
            {
            	data:"position",
            	type: 'autocomplete',
                source: ['1', '2', '3', '4'],
                strict: true,
                allowInvalid: false,
                filter: false
            },
			//入社日
            {data:"hireDate",type: 'date',dateFormat: 'YYYY/MM/DD'}
          ],
		  rowHeaders: true,      //行番号の表示
		  height: 600,           //テーブルの高さ
		  contextMenu: {
			  items: { row_above: {name: '上に行を挿入'}
		      }
		  },     //セルを右クリックしたときの行挿入を表示する。
		  columnSorting: true,    //カラムのヘッダーをクリックした際に昇順、再クリックで降順にソート
		  enterBeginsEditing: false, //true=Enterで次の行に移動する
		  search: true
		});
    }

	function postSelectedRows(form){
		//HandsOnTableで選択された行をサーバにPOST送信する。
    	var cols = htable.countCols();
    	var rows = htable.countRows();
    	list_idx=0;
    	for (i=0; i< rows; i++) {
    		//選択しているデータのみを送信する。
    		if (htable.getDataAtCell(i,0)==true){
    	    	for (j=0; j< cols; j++) {
	    			var paramname="list["+list_idx+"]." + htable.getColHeader(j);
	    			var paramvalue= htable.getDataAtCell(i,j);
	    			$("<input>",{
	    				    type:"hidden",
	    				    name:paramname,
	    				    value:paramvalue
	    			})
	    			.appendTo($(form));
    	    	}
    			list_idx++;
    		}
    	}
    	$(form).submit();
	}
	$(document).ready(function () {
		//handsOnTableのデータをサーバからAjaxをcallして取得します。
	    var urladdress=$('#posturl').val();
	    var fd = new FormData();
	    executeAjax(urladdress, "post",fd)
	    .done(function(data, textStatus, jqXHR) {
		    createHandsOnTable(data);
	    })
	    .fail(function(jqXHR, textStatus, errorThrown){
	      console.log('通信失敗');
	    });
		$("#send").click(function() {
			postSelectedRows($('#postform'));
	    	return false;
	    })
	});
	/*]]>*/
    </script>
</body>
</html>
```

createHandsOnTable・・HeloRestController#getEmpoyeeListからJSONオブジェクトをAjaxで取得してHandsOnTableにデータをセットします。
postSelectedRows・・・画面上で「checkbox」をチェックした行をフォームに追加してサーバに送信します。この処理で、input要素を生成していますが、name属性は、list[].変数名とすることで、post送信(HeloController#regist)したときに、ModelAttirbuteのフォーム属性にマッピングできるようにしています。


マッピングできれば、あとはデータベースに登録するなり処理できます。

HandsOnTableは、Excel風の編集ができるので通常の入力フォームより受けがよいと考えます。

参考にしたサイト
[Handsontable 使い方メモ３（カラム・セルオプション）](https://qiita.com/opengl-8080/items/ccf5cbccd0ceb0a6bb47)


以上