vuecliとphpを使ってHandsontableで読み込みと更新を作る

# 概要
Handsontableのvuejs用コンポーネントを利用して、サーバーからjsonファイルの読み込み
更新したデータでjsonファイルの更新を作ります。

# Handsontableとは
WEB上でExcelのような機能を提供するjsライブラリのHandsontable。
配列でデータを表現できるので便利

# フロントの表示まで作成
## 前提
[公式Vue-Cli](https://jp.vuejs.org/v2/guide/installation.html#CLI)を導入して
 http://localhost:8080/ が確認できる状態であること

## インストール
```bash
cd my-project
npm install vue-handsontable-official
npm install handsontable
npm install babel-preset-es2015
npm install jquery
```

## 実装
### html
cliでデフォルトで用意されている`index.html`に追加します。
json読み込みが終わってから表示したいので`v-if`をつけておきます。

```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Sample</title>
  </head>
  <body>
    <div id="app">

      <!-- 追加 -->
      <sample-table v-if="show" :data="tableData"></sample-table>
      <button @click="register">更新</button>
      
    </div>
  </body>
</html>
```

### vue
`src/components/SampleTable.vue`を新規作成。
[vue-handsontable-official](https://github.com/handsontable/vue-handsontable-official/wiki/Installation-guide)こちらを参考に手を加えており、`props`で外からデータを受け取れるようにしています。

```html:SampleTable.vue
<template>
    <div id="example-container" class="wrapper">
        <HotTable :root="root" :settings="hotSettings"></HotTable>
    </div>
</template>

<script>
  import HotTable from 'vue-handsontable-official';
  import Vue from 'vue';

  export default {
    data: function () {
      return {
        root: 'test-hot',
        hotSettings: {
          colHeaders: ['name', 'price'],
          rowHeaders: true,
          data: this.data
        },
      }
    },
    name: 'SampleApp',
    components: {
      HotTable
    },
    props: ['data'],
  }
</script>

<style>
    #test-hot {
        width: 600px;
        height: 400px;
        overflow: hidden;
    }
</style>
```

### js
main.jsを修正。
サーバーからのjson読み込みを想定して一旦`setTimeout`で遅延データ取得を実装。
データ取得後に`show`を`true`にすることでテーブルを表示するようにしています。
登録ボタンも一旦は`alert`のみ。

```js:main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'

// 追加
import SampleTable from './components/SampleTable.vue'
Vue.component('sample-table', SampleTable);

Vue.config.productionTip = false

// 以下書き換え
new Vue({
  el: '#app',
  data: {
    tableData: null,
    show: false
  },
  created() {
    setTimeout(() => {
      this.tableData = [{"name":"apple","price":200},{"name":"orange","price":120}];
      this.show = true;
    }, 1000);
  },
  methods: {
    register() {
      alert(JSON.stringify(this.tableData) + '登録しました！');
    }
  },
});
```

### ビルドが失敗する問題を修正
`npm run dev`時に以下のエラーが発生。

```bash
 ERROR  Failed to compile with 1 errors                                                                                                 17:09:44

 error  in ./node_modules/vue-handsontable-official/src/HotTable.vue

Module build failed: Error: Failed to find '~handsontable/dist/handsontable.full.css'
```

https://github.com/handsontable/vue-handsontable-official/issues/23
ここを見て以下実装で一旦回避。`node_modules`の中を修正しているのでイケてないですが・・

`./node_modules/vue-handsontable-official/src/HotTable.vue`

```html:HotTable.vue
// 27行目の先頭の ~ をはずす。
@import "handsontable/dist/handsontable.full.css";
```

## 確認
実行

```bash
npm run dev
```
http://localhost:8080/ 
![スクリーンショット 2018-02-05 18.06.58.png](https://qiita-image-store.s3.amazonaws.com/0/58712/cefea3b1-4df8-9419-ca7c-c7ba8fc773da.png)

表示できました。

![スクリーンショット 2018-02-05 19.50.51.png](https://qiita-image-store.s3.amazonaws.com/0/58712/e1fe0bed-5a78-3932-9ace-dcba0de83262.png)

登録ボタンの動作もOK！

# サーバー側作成とつなぎ込み
phpビルトインサーバーで簡単なjson取得と更新のAPIを作ります。

## 前提
php5.4↑が利用できる状態。

## 実装
今回は`my-project`内にフォルダを作成します。

```bash
mkdir server
cd server
```

### json
データ用の`server/tableData.json`を作成します。

```json:tableData.json
[{"name":"apple","price":100},{"name":"orange","price":120}]
```

### php
読み込み、書き込み用の`server/tableData.php`を作成します。
POST時はファイルを更新します。

```php:tableData.php
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $file = new SplFileObject('tableData.json', 'w');
    $file->fwrite(json_encode($_POST['data']));
} else {
    $file = new SplFileObject('tableData.json');
    echo $file->fgets();
}
```

### js
`src/main.js`を修正。
`created`と`register`をサーバーとのやり取りに修正します。

```js:main.js
  created() {
    $.getJSON('tableData.php', (data) => {
      this.tableData = data;
      this.show = true;
    });
  },
  methods: {
    register() {
      $.ajax({
        type: 'POST',
        url: 'tableData.php',
        data: {data: this.tableData}
      }).then(function () {
        alert('登録しました！');
      });
    }
  },
```

## フロントコードビルド
php側のサーバーで実行するためフロントコードのビルドを行って静的ファイルを作成。
サーバー側のフォルダへ移動させます。

`my-project`配下で実行します。`

```bash
cd my-project
npm run build
```

`dist`フォルダ配下にビルドされたコードがあるので、`server`フォルダへ移動します。

```bash
mv dist/* server
```

## サーバー実行
```bash
php -S localhost:3330
```

## 確認
以下の状態で更新ボタンを押すと・・・
![スクリーンショット 2018-02-05 20.36.30.png](https://qiita-image-store.s3.amazonaws.com/0/58712/c1abcaa1-9d43-7789-7d11-7198e1645c51.png)

しっかりファイルが更新されていました！
![スクリーンショット 2018-02-05 20.37.10.png](https://qiita-image-store.s3.amazonaws.com/0/58712/ab41630d-95c5-61c5-62dc-12e69dc8c1e3.png)

# まとめ
`vuecli`と`phpビルトインサーバー`を利用することでかなりサクっと実装できました。
データも配列でやり取りできるのでシンプルに読み込みと更新を作れるのも良いです。

ただ、このままだとフロントコードをビルドしつつサーバー側との疎通確認ができません。
（vuecliで実行したサーバーとphpサーバーでクロスドメイン制約にひっかかる。
ので、もっとちゃんとやるならビルドされたコードを毎回サーバー側に配置するように設定変更する必要があります。

サンプルソースは[こちら](https://github.com/naoki0531/sample-spreadsheet)。