Handsontableを入力フォームとして使って、JSONファイルを検索して結果を表示する


# はじめに
[前回](http://qiita.com/do_m_gatoru/items/86dda8dd13447988b97e)は、JSONファイルを読み込んだ結果をHandsontableで表示させた。今回は、Handsontableを入力フォームとして使って、JSONファイル内を検索して、結果を表示させてみた。
HTMLだけでやっているので、モックアップを作るときには便利そうなのでメモ。

# やってること
(1) JSONファイルを読み込む
(2) JSONファイル内のテーブル一覧をコンボボックスに表示
(3) コンボボックスを変更した場合、そのテーブルに該当するカラム名を入力フォームに展開
(4) searchボタンを押すと、コンボボックスで選択されたテーブルを検索
(5) clearボタンを押すと、画面の初期状態に戻す
(6) csvボタンを押すと、csvをダウンロードする（前回と同じ機能）


# 環境
Windows 8.1(64bit)
Internet Explorer 11

# 用意したもの
[jQuery](https://jquery.com/)
* jquery-1.11.3.min.js

[Handsontable](http://handsontable.com/)
* handsontable.full.js
* handsontable.full.css

[juantascon](https://github.com/juantascon/jquery-handsontable-csv)
* jquery.handsontable.csv.js

JSONファイル（自前）

```json:table.json
{
	"tableA":[
		{"Year": "2009", "Maserati": "0", "Mazda": "2941", "Mercedes": "4303", "Mini": "354", "Mitsubishi": "5814" },
		{"Year": "2010", "Maserati": "3", "Mazda": "2905", "Mercedes": "2867", "Mini": "412", "Mitsubishi": "5284" },
		{"Year": "2011", "Maserati": "4", "Mazda": "2517", "Mercedes": "4822", "Mini": "552", "Mitsubishi": "6127" },
		{"Year": "2012", "Maserati": "2", "Mazda": "2422", "Mercedes": "5399", "Mini": "776", "Mitsubishi": "4151" }
	],	
	"tableB":[
		{"Name": "Maserati", "Since": "1914"},
		{"Name": "Mazda", "Since": "1920"},
		{"Name": "Mercedes", "Since": "1926"},
		{"Name": "Mini", "Since": "1952"},
		{"Name": "Mitsubishi", "Since": "1970"}
	]
}
```

# 内容

## (1) JSONファイルを読み込む
## (2) JSONファイル内のテーブル一覧をコンボボックスに表示

```javascript
	$.getJSON("json/table.json", function(table) {
		jsondata = table;
		
		// コンボボックスに表示するテーブル名を取得
		tableName = new Array();
		var count = 0;
		for(key in jsondata){
			tableName[count] = key;
			count++;
		}
		
		// コンボボックスにOPTIONを追加
		for(var i = 0 ; i < tableName.length ; i++){
			$('#selectTable').append($('<option>').html(tableName[i]).val(tableName[i]));
		}
		
		// csvボタンを非活性にする
		$('#csvDownLoad').attr("disabled", "disabled");
		
	});
```

## (3) コンボボックスを変更した場合、そのテーブルに該当するカラム名を入力フォームに展開

```javascript
// コンボボックスの値を変更した場合に動く
	$('#selectTable').change(function(){
		
		// コンボボックスの値が、"NA"でなければ、テーブルを展開
		// コンボボックスの値が、"NA"の場合は、テーブルを削除
		if( $('#selectTable').val() != "NA") {
			
			// コンボボックスの値をキーにして、テーブルを取得
			var obj = jsondata[$('#selectTable').val()];
			
			// テーブルが持っている、カラム名を取得
			// データ持っている、ありき。
			var searchHeader = new Array();
			var count = 0;
			
			for(key in obj[0]){
				searchHeader[count] = key;
				count++;
			}
			
			// Handsontableに展開する配列を作成
			// 中身は空文字にする。
			var inputCell = new Array();
			for(var i = 0; i < searchHeader.length; i++){
				inputCell[i] = "";
			}
			
			var data = [inputCell]; 
				
			// 表の定義
			hot = $('#searchGrid').handsontable({  //以下はデータ指定と表示オプション
				data: data,						 //さっき作ったdataを指定
				minSpareRows: 0,				 //表の一番下にいくつ空行を表示するか
				colHeaders: searchHeader,		 //カラムの名前を表示するかどうか colheader: true/false
												 //カラムの名前を任意の名前にする colheader: 配列
				contextMenu: true
			});
		} else {
			$('#searchGrid').handsontable("destroy");
		}
	});
```

## (4) searchボタンを押すと、コンボボックスで選択されたテーブルを検索

```javascript
// searchボタンが押されたら、検索して結果を表示
	$('#search').click( function() {
		
		// コンボボックスでテーブルを選択していること。
		if( $('#selectTable').val() != "NA") {
			
			// 現在のコンボボックスの選択内容から、テーブルを取得する
			// コンボボックスの値をキーにして、テーブルを取得
			var obj = jsondata[$('#selectTable').val()];

			// searchGridから入力されているデータを取得する(配列)
			var row = $('#searchGrid').handsontable('getDataAtRow',0);
			
			// カラムを取得する
			var searchData = new Array();
			var count = 0;
			
			for(key in obj[0]){
				searchData[count] = key;
				count++;
			}
			
			// 取得したデータを元に、テーブルを検索
			var resultData = obj.filter(function(record) {
				var flag = false;
				
				for( var j = 0; j < row.length; j++ ){
					if( row[j] != "" ) {
						flag = true;
						break;
					}
				}
				
				// 検索条件が入力されていたら、JSONを検索
				// 検索条件が未入力であれば、全てを表示する
				if( flag ){
					for( var colCn = 0; colCn < searchData.length; colCn++){
						
						if( record[searchData[colCn]] === row[colCn] ){
							return true;
						}
					}
				} else {
					return true;
				}
			});
			
			// resultに展開する
			if( resultData.length > 0 ){
				if(hot2 != undefined){
					try{
						hot2.destroy();
					} catch(e) {
					}
				}
				resultData = resultData,
					container2 = document.getElementById('result'),  //後ほど表を展開する要素を指定
					hot2;
				hot2 = new Handsontable(container2, {  //以下はデータ指定と表示オプション
					data: resultData,						 //さっき作ったdataを指定
					minSpareRows: 0,				 //表の一番下にいくつ空行を表示するか
					colHeaders: searchData,			 //カラムの名前を表示するかどうか colheader: true/false
													 //カラムの名前を任意の名前にする colheader: 配列
					rowHeaders: true,
					contextMenu: true,		 		 //セルを右クリックしたときのメニューをすべて表示
					columnSorting: true				 //カラムのヘッダーをクリックした際に昇順、再クリックで降順にソート
				});
				
				// csvボタンを活性にする
				$('#csvDownLoad').removeAttr("disabled");
			} else {
				if(hot2 != undefined){
					hot2.destroy();
					hot2 = undefined;
				}
				// csvボタンを非活性にする
				$('#csvDownLoad').attr("disabled", "disabled");
				alert("検索結果がありません");
			}
		} else {
			alert("テーブルを選択してください");
		}
	});
```

## (5) clearボタンを押すと、画面の初期状態に戻す

```javascript
	// clearボタンが初期状態に戻す
	$('#clear').click(function(){
		if(hot2 != undefined){
			hot2.destroy();
			hot2 = undefined;
		}
		$('#selectTable').val("NA");
		$('#searchGrid').handsontable("destroy");
		
		// csvボタンを非活性にする
		$('#csvDownLoad').attr("disabled", "disabled");
	});
```

## 全文

```html:index.html
<!DOCTYPE html>
<html>
 
<head>
	<meta charset="Shift_JIS">
	<title>handsontable_test</title>
	<link rel="stylesheet" type="text/css" href='css/handsontable.full.css'>

	<script src='js/jquery-1.11.3.min.js'></script>   
	<script src='js/handsontable.full.js'></script>
	<script src='js/jquery.handsontable.csv.js'></script>
	<script>
$(document).ready(function () {

	var jsondata;	//JSON
	var tableName;	//テーブル一覧
	var hot2;		//resultの表定義
	
	// 画面読み込み時、table.jsonの内容を取得する
	$.getJSON("json/table.json", function(table) {
		jsondata = table;
		
		// コンボボックスに表示するテーブル名を取得
		tableName = new Array();
		var count = 0;
		for(key in jsondata){
			tableName[count] = key;
			count++;
		}
		
		// コンボボックスにOPTIONを追加
		for(var i = 0 ; i < tableName.length ; i++){
			$('#selectTable').append($('<option>').html(tableName[i]).val(tableName[i]));
		}
		
		// csvボタンを非活性にする
		$('#csvDownLoad').attr("disabled", "disabled");
		
	});
	
	// コンボボックスの値を変更した場合に動く
	$('#selectTable').change(function(){
		
		// コンボボックスの値が、"NA"でなければ、テーブルを展開
		// コンボボックスの値が、"NA"の場合は、テーブルを削除
		if( $('#selectTable').val() != "NA") {
			
			// コンボボックスの値をキーにして、テーブルを取得
			var obj = jsondata[$('#selectTable').val()];
			
			// テーブルが持っている、カラム名を取得
			// データ持っている、ありき。
			var searchHeader = new Array();
			var count = 0;
			
			for(key in obj[0]){
				searchHeader[count] = key;
				count++;
			}
			
			// Handsontableに展開する配列を作成
			// 中身は空文字にする。
			var inputCell = new Array();
			for(var i = 0; i < searchHeader.length; i++){
				inputCell[i] = "";
			}
			
			var data = [inputCell]; 
				
			// 表の定義
			hot = $('#searchGrid').handsontable({  //以下はデータ指定と表示オプション
				data: data,						 //さっき作ったdataを指定
				minSpareRows: 0,				 //表の一番下にいくつ空行を表示するか
				colHeaders: searchHeader,		 //カラムの名前を表示するかどうか colheader: true/false
												 //カラムの名前を任意の名前にする colheader: 配列
				contextMenu: true
			});
		} else {
			$('#searchGrid').handsontable("destroy");
		}
	});
	
	// searchボタンが押されたら、検索して結果を表示
	$('#search').click( function() {
		
		// コンボボックスでテーブルを選択していること。
		if( $('#selectTable').val() != "NA") {
			
			// 現在のコンボボックスの選択内容から、テーブルを取得する
			// コンボボックスの値をキーにして、テーブルを取得
			var obj = jsondata[$('#selectTable').val()];

			// searchGridから入力されているデータを取得する(配列)
			var row = $('#searchGrid').handsontable('getDataAtRow',0);
			
			// カラムを取得する
			var searchData = new Array();
			var count = 0;
			
			for(key in obj[0]){
				searchData[count] = key;
				count++;
			}
			
			// 取得したデータを元に、テーブルを検索
			var resultData = obj.filter(function(record) {
				var flag = false;
				
				for( var j = 0; j < row.length; j++ ){
					if( row[j] != "" ) {
						flag = true;
						break;
					}
				}
				
				// 検索条件が入力されていたら、JSONを検索
				// 検索条件が未入力であれば、全てを表示する
				if( flag ){
					for( var colCn = 0; colCn < searchData.length; colCn++){
						
						if( record[searchData[colCn]] === row[colCn] ){
							return true;
						}
					}
				} else {
					return true;
				}
			});
			
			// resultに展開する
			if( resultData.length > 0 ){
				if(hot2 != undefined){
					try{
						hot2.destroy();
					} catch(e) {
					}
				}
				resultData = resultData,
					container2 = document.getElementById('result'),  //後ほど表を展開する要素を指定
					hot2;
				hot2 = new Handsontable(container2, {  //以下はデータ指定と表示オプション
					data: resultData,						 //さっき作ったdataを指定
					minSpareRows: 0,				 //表の一番下にいくつ空行を表示するか
					colHeaders: searchData,			 //カラムの名前を表示するかどうか colheader: true/false
													 //カラムの名前を任意の名前にする colheader: 配列
					rowHeaders: true,
					contextMenu: true,		 		 //セルを右クリックしたときのメニューをすべて表示
					columnSorting: true				 //カラムのヘッダーをクリックした際に昇順、再クリックで降順にソート
				});
				
				// csvボタンを活性にする
				$('#csvDownLoad').removeAttr("disabled");
			} else {
				if(hot2 != undefined){
					hot2.destroy();
					hot2 = undefined;
				}
				// csvボタンを非活性にする
				$('#csvDownLoad').attr("disabled", "disabled");
				alert("検索結果がありません");
			}
		} else {
			alert("テーブルを選択してください");
		}
	});
	
	// csvボタンがクリックされたら表の内容をCSV出力する
	$('#csvDownLoad').click(function(){
		
		var csv_string = handsontable2csv.string(hot2);
	
		var bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
		var blob = new Blob([bom, csv_string], { type: 'text/csv'});
	
		// IE独自関数
		window.navigator.msSaveBlob(blob, "test.csv");
	});
	
	// clearボタンが初期状態に戻す
	$('#clear').click(function(){
		if(hot2 != undefined){
			hot2.destroy();
			hot2 = undefined;
		}
		$('#selectTable').val("NA");
		$('#searchGrid').handsontable("destroy");
		
		// csvボタンを非活性にする
		$('#csvDownLoad').attr("disabled", "disabled");
	});
	
	// Handsontableのおまじない
	//以下は基本的にいじらない
	function bindDumpButton() {

		Handsontable.Dom.addEvent(document.body, 'click', function (e) {

			var element = e.target || e.srcElement;

			if (element.nodeName == "BUTTON" && element.name == 'dump') {
				var name = element.getAttribute('data-dump');
				var instance = element.getAttribute('data-instance');
				var hot = window[instance];
				console.log('data of ' + name, hot.getData());
			}
		});
	}
	//関数実行
	bindDumpButton();
});
	</script>
</head>

<body>
	<p>テーブル選択</p>
	<select style="width:200px;" id="selectTable">
	<option value="NA"></option>
	</select>
	<div id="searchGrid" class="handsontable"></div><br>
	<input id="search" type="button" value="search"/>
	<input id="clear" type="button" value="clear"/>

	<hr>
	<div id="result" class="handsontable"></div><br>
	<input id="csvDownLoad" type="button" value="csv"/>
</body>
</html>
```

# 最後に
わかりにくい点、間違っている点、効率的に書ける箇所などあれば教えていただければ幸いです。