Vue+LIFF(LINE Front-end Framework)+GASでフォームを使ったBOTを作ってみた（その１：Vue編）

LIFFを使用したアプリケーションを何か作ってみたかったので、勤怠連絡のBOTを作ってみました。
ちょっと長くなりそうなので、全3回でまとめます。

- [Vue+LIFF(LINE Front-end Framework)+GASでフォームを使ったBOTを作ってみた（その１：Vue編）](https://qiita.com/korsmic/items/0009a020cc18e20c7406)
- [Vue+LIFF(LINE Front-end Framework)+GASでフォームを使ったBOTを作ってみた（その２：LIFF編）](https://qiita.com/korsmic/items/5e9c5e65f3023fe806f1)
- [Vue+LIFF(LINE Front-end Framework)+GASでフォームを使ったBOTを作ってみた（その３：GAS編）](https://qiita.com/korsmic/items/3615e3a59cb87c6e620d)

[githubはこちら](https://github.com/korsmic/vue-liff-bot)

## 出来上がりはこんな感じ

![1.jpg](https://qiita-image-store.s3.amazonaws.com/0/83008/e0a9a0e7-c489-d0a9-a6a0-b3e4efa0a4b2.jpeg)
![2.jpg](https://qiita-image-store.s3.amazonaws.com/0/83008/e335315e-490d-4f86-6c2f-ad96a2cac0cf.jpeg)
![3.jpg](https://qiita-image-store.s3.amazonaws.com/0/83008/ae444e7b-a71b-181a-785a-f9ff9a9cc6ba.jpeg)
![4.jpg](https://qiita-image-store.s3.amazonaws.com/0/83008/6a5f2162-6190-dfd6-6fe5-b8802cbe0d83.jpeg)
![5.jpg](https://qiita-image-store.s3.amazonaws.com/0/83008/4cf91b3d-b333-a8e4-699f-b818274acaf6.jpeg)




## 開発準備
フロントは、jQueryでもよかったのですが、せっかくなのでVue.jsの勉強のためVue.jsで構築しています。

### Vue CLI3をインストール

まずは、[Vue CLI3](https://cli.vuejs.org/)をインストールします。

インストール

```
npm install -g @vue/cli
# OR
yarn global add @vue/cli
```

作成（設定はデフォルトのままで大丈夫です）

```
vue create vue-liff-bot
```

パッケージのインストール

```shell
yarn install
```

### bootstrap-vueを追加

今回は、BootStrapを利用するので、BootStrapもインストール

```shell
yarn add bootstrap-vue
```

### 開発スタート

以下で開発を始めます。

```shell
yarn run serve
```

## bootstrap-vueの組み込み

`main.js`に`bootstrap-vue`を組み込みます。

```javascript:main.js
import Vue from 'vue'
import App from './App.vue'

// BottstrapVueの読み込み
import BootstrapVue from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

Vue.config.productionTip = false

// BootstrapVueの使用
Vue.use(BootstrapVue)

new Vue({
  render: h => h(App)
}).$mount('#app')

```

## Vueファイルの作成

### App.vue

まずは、App.vueを変更します。

```javascript:src/App.vue

<template>
  <div id="app" class="pt-3 pb-3">
    <input-form :ttl="title" />
  </div>
</template>

<script>
// inputFormをComponentとして設定
import InputForm from '@/components/InputForm.vue'

export default {
  name: 'app',
  components: {
    InputForm
  },
  data() {
    return {
      // titleをpropとしてComponentに渡す
      title : "勤怠連絡 BOT"
    }
  }
}
</script>

<style>
#app {
  color: #2c3e50;
}
</style>
```

### InputForm.vue

続いて、InputForm.vueを作成します。

```javascript:src/components/InputForm.vue
<template>
  <div class="container">
    <!-- 親のpropsを受け取る -->
    <h1 class="text-center pb-2">{{ttl}}</h1>
    <!-- submitイベントを無効に -->
    <form @submit.prevent="">
      <div class="form-group">
        <label for="name">名前</label>
        <input type="text" class="form-control" id="name" v-model="data.name" @change="createMessage()">
      </div>
      <div class="form-group">
        <label for="reason">遅刻理由</label>
        <select class="form-control" v-model="data.reason" id="reason" @change="createMessage()">
            <option v-for="(reason, index) in reasons" :key="index">{{reason}}</option>
          </select>
      </div>
      <!-- 理由がその他の時のみ表示 -->
      <div class="form-group" v-show="data.reason=='その他'">
        <input type="text" class="form-control" id="reson_other" @change="createMessage()" placeholder="その他の場合は理由を書いてください。" v-model="data.reason_other">
      </div>
      <div class="form-group">
        <label>種別</label>
        <div class="form-check" v-for="(type, index) in types" :key="index">
          <input class="form-check-input" type="radio" v-model="data.schedule" :id="index" :value="type" :checked="((index!=0)? '': 'checked')" @change="createMessage()">
          <label class="form-check-label" :for="index">{{type}}</label>
        </div>
      </div>
      <!-- 種別が遅刻しますの時のみ表示 -->
      <div class="form-group" v-show="data.schedule == '遅刻します'">
        <label>遅刻時間</label>
        <div class="form-row align-items-center">
          <div class="col-5 form-group">
            <select class="form-control" v-model="data.time1" @change="createMessage()">
              <!-- createTimes()の配列を設定 -->
              <option v-for="(time, index) in createTimes(0,12)" :key="index">{{time}}</option>
            </select>
          </div>
          <div class="col-2 text-center form-group">
            <span>～</span>
          </div>
          <div class="col-5 form-group">
            <!-- createTimes()の配列を設定 -->
            <select class="form-control" v-model="data.time2" @change="createMessage()">
              <option v-for="(time, index) in createTimes(1,13)" :key="index">{{time}}</option>
            </select>
          </div>
        </div>
      </div>
      <div class="form-group">
        <label for="message">メッセージ</label>
        <!-- createMessageで生成されたデータを挿入 -->
        <textarea class="form-control" id="message" rows="5" v-model="data.message"></textarea>
      </div>
      <div class="form-group pt-2">
        <button class="btn btn-primary btn-lg btn-block" id="send" type="submit">送信</button>
      </div>
    </form>
  </div>
</template>

<script>
  export default {
    name: 'InputForm',
    props: {
      ttl: String
    },
    data() {
      return {
        data: {
          name: '',
          reason: '',
          reason_other: '',
          schedule: '',
          time1: '',
          time2: '',
          message: '',
        },
        // 遅刻理由の中身
        reasons: ['電車遅延', '体調不良', '忘れ物', 'その他'],
        // 種別の中身
        types: ['遅刻します', '全休をいただきます', '午前休をいただきます', '午後休をいただきます'],
        // メッセージのテンプレート
        text1: 'おはようございます。',
        text2: 'よろしくお願いします。'
      };
    },
    methods: {
      // 時間のオプションを配列で生成
      createTimes: function(init, to) {
        const times = [];
        for (let index = init; index < to; index++) {
          times.push(`${index * 5}分`);
        }
        return times;
      },
      // inputに変更があった場合にメッセージの作成
      createMessage: function() {
        let text, text_reson, text_schedule
  
        // その他の場合は、reason_otherのデータを使用
        if (this.data.reason == "その他") {
          text_reson = this.data.reason_other
        } else {
          text_reson = this.data.reason
        }

        // 遅刻の場合は時間に入力した時間を使用して生成
        if (this.data.schedule == "遅刻します") {
          text_schedule = `${this.data.time1}～${this.data.time2}ほど${this.data.schedule}`
        } else {
          text_schedule = this.data.schedule
        }

        // メッセージを生成
        text = `${this.text1}${this.data.name}です。${text_reson}のため${text_schedule}。${this.text2}`;

        // データに渡す
        this.data.message = text;
      },
    }
  };
</script>

<style>
  
</style>

```

`input`、`select`の変更を検知して、`data`を更新＋`message`を都度生成します。


## 完成！

以下のコマンドでビルドします。

```shell
yarn run build
```


## まとめ

- `v-model`がめっちゃくちゃ便利。
- イベントの設定にすべての箇所で`@change="createMessage()`を入れて、あまりイケてない感じがしますがとりあえずこのままになっちゃってます。`forEach`とかでまとめてイベントをつけてあげるといいのかなと思います。


次はLIFFの設定になります。

- [Vue+LIFF(LINE Front-end Framework)+GASでフォームを使ったBOTを作ってみた（その２：LIFF編）](https://qiita.com/korsmic/items/5e9c5e65f3023fe806f1)