ふぉーむ
Warning: Attempt to read property "site_name" on bool in /home/goodhokkaido/sixwheel.net/public_html/muguruma/wp/wp-content/themes/frame/functions.php on line 208
Warning: Attempt to read property "title" on bool in /home/goodhokkaido/sixwheel.net/public_html/muguruma/wp/wp-content/themes/frame/functions.php on line 211
Warning: Attempt to read property "description" on bool in /home/goodhokkaido/sixwheel.net/public_html/muguruma/wp/wp-content/themes/frame/functions.php on line 217
Nuxt3でNetlify Formsを動かしてみる
Nuxt3をSSRで動かすために、Netlifyにデプロイした際、フォームもNetlify上で動くように色々試したので、メモ。確認画面やサンキュー画面への遷移はjsで操作したかったので、js操作で送信できる方法を探しました。実行環境は以下です。
nuxt 3.6.5
netlify-cli 17.16.0
node v20.10.0
typescript 5.3.2
JsでFetchする
下記を読むと、NetlifyでFormを動かすにはいくつかの方法があるようです。私の場合、事前にSSGのために作ったプロジェクトがあり、送信機能はphpが担っていました。その部分をNetlifyに担当してもらおうと考えています。そのため、基本はベースを活かして、Netlifyで動くようにカスタマイズする形で作業しました。
上記サイトにある下記のSubmit JavaScript-rendered forms with AJAXのコードをもとに、自分のプロジェクトで使っていたtsをアレンジし、組み込むことに。そんなに手間がかからず対応できました。
const handleSubmit = (event) => {
event.preventDefault();
const myForm = event.target;
const formData = new FormData(myForm);
fetch("/", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams(formData).toString(),
})
.then(() => navigate("/thank-you/"))
.catch((error) => alert(error));
};
<form
data-netlify="true"
name="pizzaOrder"
method="post"
onSubmit={handleSubmit}
>
<input type="hidden" name="form-name" value="pizzaOrder" />
<label>
What order did the pizza give to the pineapple?
<input name="order" type="text" onChange={handleChange} />
</label>
<input type="submit" />
</form>
なんか動かない
それを当てはめてみましたが、動かないし、tsエラーも発生します。const myForm = event.targetのところで値をとれていないようなので、console.logでeventをnetlify devで見てみることに。すると、かなり奥まった場所に、submit◯◯というinputやtextareaで使っているnameと同じ名前の値が取れている場所があります。※submitも@click=”submit”だったので、おそらくsubmitになっていると予想。個人の名付けによって異なるハズです。ここから直接値を取れないかcopilotに聞いてみるとそれらはVueが生成する値くらいしか回答は得られませんでした。試しに、console.logしてみると、その名前だけで値が取れるのがわかりました。これは便利ですね。
次にエラーが出たのが、bodyのところ。typescriptのバージョンによっては、body: new URLSearchParams(formData).toString()が使えないと教えてくれます。そして、個別で拾うしかないとも。結果、typescriptエラーを回避しながらできたのが以下です。form-nameはformタグで指定したnameで、contents、userName、emailはinputやtextareaで指定したnameです。これらは必須項目で、任意項目はif文でありなしを判定し、inputで指定したname=”place”に値が入っていれば、FormDataに追加する形にしました。
const submit = (event: Event) => {
event.preventDefault()
console.log(event)
const formData = new FormData()
formData.append('form-name', 'contact')
formData.append('contents', submitContents)
formData.append('userName', submitUserName)
formData.append('email', submitEmail)
if (submitPlace) {
formData.append('place', submitPlace)
}
console.log(submitContents, submitUserName, submitEmail, submitPlace)
const params = new URLSearchParams();
formData.forEach((value, key) => {
if (typeof value === 'string') {
params.append(key, value)
}
})
fetch('/inquiry', {
method: 'POST',
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: params.toString(),
})
.then((response) => {
if (response.ok) {
return response.text()
} else {
throw new Error(response.statusText)
}
})
.then((result) => {
console.log(result)
})
.catch((error) => {
console.error(error)
})
}
さて、そろそろメールが取得できるかなと管理画面を確認しますが、なんの変化もありません。もちろん、管理画面上でもForm detection is enabledですし、受け入れ体制は万全なハズ。フォームもサンキュー画面に移行しているので、大きな問題ではなさそう。コンソールでもエラーは出ていないので、ボタンの掛け違いくらいな間違いの印象です。で、そこから色々検索し、フォームに加えたり、削ったりしました。下記がそのフォーム部分。VeeValidateをいれているので、その部分はそのままです。ボタンが4つあるのは、遷移中にボタンを隠したり、出したりするため。送信ボタンだけがフォームのトリガーです。
<form name="contact" method="POST" data-netlify="true" data-netlify-honeypot="bot-field">
<input type="hidden" name="form-name" value="contact" />
<p hidden>
<label>Do not fill this out: <input name="bot-field" /></label>
</p>
<dl class="inquiry">
<dt class="inquiry__ttl">
<span class="required box__choice">必須</span>
<label for="contents">内容</label>
</dt>
<dd class="inquiry__content">
<textarea id="contents" v-model="contents" name="contents" class="inquiry__content--textarea input__appearance confirm__element"></textarea>
<span v-if="errors.contents" class="invalid confirm__element">{{ errors.contents }}</span>
</dd>
<dt class="inquiry__ttl">
<span class="required box__choice">必須</span>
<label for="userName">名前</label>
</dt>
<dd class="inquiry__content">
<input id="userName" v-model="userName" name="userName" class="inquiry__content--input input__appearance confirm__element" />
<span v-if="errors.userName" class="invalid confirm__element">{{ errors.userName }}</span>
</dd>
<dt class="inquiry__ttl">
<span class="required box__choice">必須</span>
<label for="email">メールアドレス</label>
</dt>
<dd class="inquiry__content">
<input id="email" v-model="email" name="email" class="inquiry__content--input input__appearance confirm__element" />
<span v-if="errors.email" class="invalid confirm__element">{{ errors.email }}</span>
</dd>
<dt class="inquiry__ttl">
<span class="optional box__choice">任意</span>
<label for="place">在住の区市町村</label>
</dt>
<dd class="inquiry__content">
<input id="place" name="place" class="inquiry__content--input input__appearance place confirm__element" />
</dd>
</dl>
<ul class="btns">
<li class="btn__wrap">
<button class="button confirm" type="button" @click="confirm">確認</button>
</li>
<li class="btn__wrap">
<button class="button reset" type="button" @click="reset">白紙</button>
</li>
<li class="btn__wrap">
<button class="button back" type="button" @click="back">修正</button>
</li>
<li class="btn__wrap">
<button class="button submit" type="button" @click="submit">送信</button>
</li>
</ul>
</form>
ここまで、.vueファイルだけで、進めてきました。formタグも、トリガーもあるので、心の奥底では行けんじゃねと思ってましたが、メールは届きません。やはり、足りないみたいです。.htmlファイルを作ることを避けてきましたが、しょうがない試しにやってみるかと、ダミーHTMLの作り方を探します。最終的に役立ったのがこちらの記事。やはり、ダミーHTMLは必須のようで、publicフォルダにinquiry.htmlとして作成。FetchのURLをそのダミーHTMLに変更すると、ついに管理画面にメールが現れました。めでたし、めでたし。
<form name="contact" method="POST" data-netlify="true">
<input type="hidden" name="form-name" value="contact" />
<textarea name="contents"></textarea>
<input type="text" name="userName" />
<input type="email" name="email" />
<input type="text" name="place" />
</form>