Nuxt.js・Netlify・Vuetify・VeeValidate・axiosでフォーム機能を実装してみた

Nuxt.jsで作ったサイトをNetlifyにデプロイしてフォーム機能を実装したので、その方法をまとめて見ました。
UIフレームワークにはVuetify、フォームのバリデーションにはVeeValidateを使用しています。
フォームのpostには、axiosを使用します。
完成イメージは下記の画像になります。
![form.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559347/c83f5ac1-23ac-0279-b6d3-f3b7b21a38be.gif)


## VeeValidateをインストール
まずは、フォームのバリデーションの為に[VeeValidate](https://logaretm.github.io/vee-validate/)をインストールします。

```
#npm
npm install vee-validate --save

#yarn
yarn add vee-validate
```
### pluginsディレクトリにてVeeValidateの設定
```
├─ plugins
│  ├─ veeValidate.js
```

```js:veeValidate.js
import Vue from 'vue'
import {
  extend,
  ValidationProvider,
  ValidationObserver,
  localize
} from 'vee-validate'
import ja from 'vee-validate/dist/locale/ja.json'
import { required, email, max } from 'vee-validate/dist/rules'

localize('ja', ja)

extend('required', required) // 入力必須項目

extend('email', email) // アドレス用

extend('max', max) //最大入力数

Vue.component('ValidationProvider', ValidationProvider)
Vue.component('ValidationObserver', ValidationObserver)

```

### nuxt.config.jsで読み込む
```js:nuxt.config.js
export default {
   plugins: [{ src: '~/plugins/veeValidate.js' }]
}

```

##フォームを作成
```vue:Form.vue
<template>
  <div class="form">
    <ValidationObserver
      ref="observer"
      v-slot="{ invalid, validated }"
      @submit.prevent="validateForm"
      tag="form"
      name="contact"
      method="post"
      netlify <!--netlifyの設定-->
      data-netlify-honeypot="bot-field" <!--netlifyの設定-->
    >
      <!--netlifyの設定--><input type="hidden" name="form-name" value="contact" /> <!--netlifyの設定-->

      <div class="form__wrapper">
        <ValidationProvider v-slot="{ errors }" name="Name" rules="required|max:20">
          <v-text-field
            v-model="name"
            :error-messages="errors"
            label="Name"
            type="text"
            name="name"
          ></v-text-field>
        </ValidationProvider>
      </div>

      <div class="form__wrapper">
        <ValidationProvider v-slot="{ errors }" name="Email" rules="required|email">
          <v-text-field
            v-model="email"
            :error-messages="errors"
            label="E-mail"
            type="email"
            name="email"
          ></v-text-field>
        </ValidationProvider>
      </div>

      <div class="form__wrapper">
        <ValidationProvider v-slot="{ errors }" name="Contact" rules="required">
          <v-textarea
            label="Contact"
            :error-messages="errors"
            v-model="text"
            name="text"
          ></v-textarea>
        </ValidationProvider>
      </div>

      <div class="form__wrapper">
        <ValidationProvider v-slot="{ errors }" name="check" rules="required">
          <v-checkbox
            v-model="checkbox"
            :error-messages="errors"
            label="送信しますか？"
            type="checkbox"
            value="1"
            class="checkbox"
          ></v-checkbox>
        </ValidationProvider>
      </div>

 <!--netlifyの設定-->
      <div v-show="false"> 
        <label for="message">スパム対策</label>
        <input type="text" name="bot-field" v-model="botField" />
      </div>
 <!--netlifyの設定-->

      <v-btn @click="clearForm">リセット</v-btn>
      <v-btn type="submit">送信</v-btn>

    </ValidationObserver>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  data() {
    return {
      name: '',
      email: '',
      text: '',
      checkbox: null,
      botField: '',

    }
  },
  methods: {
    //バリデーション用のメソッド
    validateForm() {
      const observer: any = this.$refs.observer
      const result = await observer.validate()
      if (!result) {
        return
      }
      //バリデーションを通過すればsubmitForm()を呼び出す
      this.submitForm()
    },
     //次の章で追加
     submitForm() {
    },
    //クリア用のメソッド。リセットボタンを押すとフォームを空白に戻します。
    clearForm() {
      const observer: any = this.$refs.observer
      this.name = ''
      this.email = ''
      this.text = ''
      this.checkbox = null
      observer.reset() //バリデーションをリセット
    }
  }
})
</script>
```

`<!--netlifyの設定-->`と記述した部分はNetlifyとの連携部分になります。
[NetlifyのFormのドキュメント](https://docs.netlify.com/forms/setup/)

`@submit.prevent`とすることで、submitをするのをストップしています。
`@submit.prevent="validateForm"`でメソッドを呼び出して、axiosでフォームをpostする為です。

##axiosでnetlifyにフォームを送信する

```vue:Form.vue
<script lang="ts">
import Vue from 'vue'
import axios from 'axios'

export default Vue.extend({
  data() {
    return {
      name: '',
      email: '',
      text: '',
      checkbox: null,
      botField: ''
    }
  },
  methods: {
    async validateForm() {
      const observer: any = this.$refs.observer
      const result = await observer.validate() //バリデーションのチェック。入力されていない項目があれば、処理を止める
      if (!result) {
        return
      }
      this.submitForm()
    },
    async submitForm() {
      const params = this.setParams()
      const response: any = await axios.post('/', params).catch((error) => {
        error.response
      })
    },
    //送信する項目をセット。
    setParams() {
      const params = new URLSearchParams() 
      params.append('form-name', 'contact')
      params.append('name', this.name)
      params.append('email', this.email)
      params.append('text', this.text)
      if (this.botField) {
        params.append('bot-field', this.botField)
      }
      return params
    }
})
</script>
```
`new URLSearchParams() `を使い、フォームの内容をセットしています。
そして、axiosでpostを実行しています。

## 送信が完了すれば、スナックバーでお知らせする機能を追加
Vuetifyのスナックバーを使って、送信が上手く行った場合にポップアップでお知らせをする機能を追加します。

```vue:Form.vue
<template>
  <div class="form">
    <!-- 省略 -->
    
    <!-- 追加 -->
    <v-snackbar v-model="completeForm" timeout="3000" vertical color="primary">  <!-- 送信成功時 -->
      送信を完了しました。
      <template v-slot:action="{ attrs }">
        <v-btn text v-bind="attrs" @click="completeForm = false">
          Close
        </v-btn>
      </template>
    </v-snackbar>

    <v-snackbar v-model="errorForm" timeout="5000" vertical color="error"> <!-- 送信エラー時 -->
      送信でエラーが発生しました。
      <template v-slot:action="{ attrs }">
        <v-btn text v-bind="attrs" @click="errorForm = false">
          Close
        </v-btn>
      </template>
    </v-snackbar>
   <!-- 追加 -->
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import axios from 'axios'

export default Vue.extend({
  data() {
    return {
      name: '',
      email: '',
      text: '',
      checkbox: null,
      botField: '',
      completeForm: false,
      errorForm: false
    }
  },
  methods: {
    async validateForm() {
      const observer: any = this.$refs.observer
      const result = await observer.validate()
      if (!result) {
        return
      }
      this.submitForm()
    },
    async submitForm() {
      const params = this.setParams()
      const response: any = await axios.post('/', params).catch((error) => {
        error.response
        //エラーだった場合は、エラー用のスナックバーを表示
        this.errorForm = true
      })
      //送信が完了した後の処理を追加
      if (response.status === 200) {
        this.completeForm = true
        this.clearForm()
      } else {
      //エラーだった場合は、エラー用のスナックバーを表示
        this.errorForm = true
      }
    }
})
</script>

```
上記を追加することで、今回のフォームの完成です。
![form.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559347/c83f5ac1-23ac-0279-b6d3-f3b7b21a38be.gif)


## 最後に
Nuxt.jsとNetlifyの組み合わせはかなり便利ですね。ホスティングも簡単ですし、フォーム機能も使いやすいです。

### 参考サイト
[Nuxt.js + Netlifyで問い合わせフォームを作る方法【自動返信あり】](https://notes.sharesl.net/articles/1711/)