Nuxt+VuetifyなSPAから、Netlify Formsを使う方法。

#きっかけ
どこかのサイト：「Netlify Formsを使うと、フォーム機能を手軽に作成できます。」

自分：「なるほど。やってみよう。」

（**Nuxt+Vuetify**で作ったSPAにお問い合わせフォームをつけようとする）

自分：「わからん。ズバリなサンプルコードも見つからん。:innocent:」

自分：「自力で開拓するしかないな...」


#環境
`vue@2.6.11` `nuxt@2.11.0` `vuetify@2.1.4`

#プレーンなHTMLの場合
プレーンなHTMLから Netlify Forms にPOSTする仕組みの作り方は、`form`タグに`netlify`属性をつけるだけ。
例えば以下のようなHTMLを書いてNetlifyにアップ[^netlify-up]すると、これを自動的に解析してPOSTできるようにしてくれます。

[^netlify-up]: Netlifyと連携してあるGitHubのリポジトリにpush


```html:sample.html
<!-- 抜粋 -->
    <form name="contact" method="POST" netlify>
      <input type="text" name="name" />
      <input type="text" name="email" />
      <input type="text" name="company" />
      <textarea name="message"></textarea>
      <button type="submit">送信</button>
    </form>
```

たったこれだけ。お手軽ですね！

でも、自分が作りたいのはNuxtベースのSPAなんです。しかもVuetifyを適用。単純には行きません...


#Nuxt+Vuetifyの場合
次のことがわかっていないので、どうにか解決する必要があります。

1. どうやってNetlifyにフォームの存在とその内容（項目）を伝えるか
2. VuetifyのフォームからPOSTするときにフォームデータをどのように取り扱うか

ひとつずつ見ていきます。

##1. どうやってNetlifyにフォームの存在とその内容（項目）を伝えるか

ダミーのHTMLを使います。このこと自体は[Netlifyの公式ブログ](https://www.netlify.com/blog/2018/09/07/how-to-integrate-netlify-forms-in-a-vue-app/)にも載っています。
ただ、Nuxtでの方法までは書いてありません。どこのフォルダに置くかとか。

Vue単体での事例やReactでの事例などを参考にさせていただきつつ試行錯誤した結果、以下のようにすればOKということがわかりました。

- Nuxt の static フォルダ配下にHTMLを配置
 - 名前は任意でよいので、今回は`dummy-contact-form.html`にしました。
- form要素はname属性とnetlify属性が重要。action属性やmethod属性は不要。
- hidden属性をつけておくとhtmlを直接表示してもなにも見えないのでいい感じ。[^hidden]

[^hidden]: もちろんデベロッパーツールを使えばHTMLの中身は見えます。それも避けるなら、Netlify側でリダイレクトの設定を入れておけば良いようです。


```vue:static/dummy-contact-form.html
<!DOCTYPE html>
<html>
  <body>
    <!-- 完全にダミーフォーム専用 -->
    <form name="contact" netlify netlify-honeypot="bot-field" hidden>
      <input type="text" name="name" />
      <input type="text" name="email" />
      <input type="text" name="company" />
      <textarea name="message"></textarea>
      <input type="text" name="bot-field" />
    </form>
  </body>
</html>
```

これをNetlifyにアップすると、Formsが生成されます。

<img width="280" alt="スクリーンショット 2020-02-09 21.37.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120785/a3cf7dcb-11dd-aaf1-35ca-edb0089dafce.png">

<details><summary>netlify-honeypot について</summary>
上記例では、botよけとして`netlify-honeypot="bot-field"`の設定も入れました。
これは、netlify-honeypot属性で指定した項目名のフォーム（ここでは`bot-field`）に何か入力があったら、それはbotの投稿とみなして投稿がブロックされます。
Forms の Spam submissions にすら入りません。
</details>

##2. Nuxt+VuetifyからPOSTするときにフォームデータをどのように取り扱うか
Nuxt(SPA)からのPOSTなのでaxiosを使ってJavaScriptから非同期で投稿します。
また、Vuetifyを使っているので、`input`ではなく`v-text-field`、`textarea`ではなく`v-textarea`を使うことになります。[^v-form]

[^v-form]: ちなみに、v-formはフォームのバリデーションを手軽に実装するためのコンポーネントなので、通常のformとは意味合いが異なります。

具体的には、以下のようにしました。

- `form-name`に、ダミーフォームのform.nameと同じ内容を設定する
- 各項目の名前もダミーフォームと一致させる
 - `params.append`するときの名前が一致していればOKです。

```vue:pages/contact.vue
<template>
  <!-- 抜粋です -->
  <v-text-field
    v-model="name"
    label="お名前"
    required
  />
  <v-text-field
    v-model="email"
    label="メールアドレス"
    required
  />
  <v-text-field
    v-model="company"
    label="会社名（法人の方のみ）"
  />
  <v-textarea
    v-model="message"
    label="お問い合わせ内容"
    required
  />
  <v-text-field
    v-model="botfield"
    label="人間は入力しないでください"
    v-show="false"
  />
  <v-btn color="primary" @click="submit">送信</v-btn>
</template>

<script>
export default {
  data() {
    return {
      name: "",
      email: "",
      company: "",
      message: "",
      botfield: "",
    }
  },
  methods: {
    async submit() {
      const params = new FormData()
      //以下、ダミーフォームの各フォーム要素のnameと合わせる
      params.append('form-name', 'contact')
      params.append('name', this.name)
      params.append('email', this.email)
      params.append('company', this.company)
      params.append('message', this.message)
      params.append('bot-field', this.botfield)

      const response = await this.$axios.$post(window.location.origin, params)
      //実際はresponseを使って画面側にフィードバックさせるが、ここでは仮にconsoleに出力
      console.log(response)
    },
  },
}
</script>
```

※実際はresponseを使って画面側にフィードバックさせる必要がありますが、ここでは仮にconsoleに出力するだけにしています。また、バリデーションのコードも必要になるでしょう。

##結果
画面はこのようになりました。（適当な値を入力したあとのキャプチャです）
<img width="434" alt="スクリーンショット 2020-02-09 23.15.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120785/9e46af9b-61b8-60ad-86bd-da47e7b87b58.png">

送信したところ、投稿した内容もちゃんとFormsに入りました:tada:
<img width="279" alt="スクリーンショット 2020-02-09 23.31.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120785/80694caa-06f6-ad4d-be5f-b074be74a05e.png">

#まとめ
Nuxt+Vuetify な SPA から、 Netlify Forms を使うときの対応方法。

1. static配下にプレーンなHTMLでダミーフォームを作成し、Netlifyにフォームの存在を認識させる。
2. そのフォームと項目を一致させる形で、axiosからFormDataを投げる。

#余談
この仕組みを作るとき、axiosのbaseURL関係でもハマりました。
上記コードでしれっと`this.$axios.$post(window.location.origin, params)`と書いているところ。
それについては別の記事を~~書こうと考えています。~~[書きました。(2020.02.12)](https://qiita.com/shozzy/items/69706cd550b407526884)

#参考
https://www.netlify.com/blog/2018/09/07/how-to-integrate-netlify-forms-in-a-vue-app/
https://qiita.com/yahsan2/items/a70c4c8f617ee9b1f9ff
https://qiita.com/miiina016/items/e86c0bf4f8808dd6b6ae
https://qiita.com/nanaki14/items/007eae905d6305f75f6a
https://qiita.com/tatane616/items/f646e84fe4cdd9eac0de