Laravel Inertia では、フォーム使用を簡単にするための useForm
という関数が用意されている
これの使い方をまとめた記事
いつもの環境構築
$ curl -s https://laravel.build/example-app?with=pgsql | bash
$ cd example-app
$ ./vendor/bin/sail up -d
$ ./vendor/bin/sail composer require laravel/breeze --dev
$ ./vendor/bin/sail artisan breeze:install
$ ./vendor/bin/sail artisan migrate
$ ./vendor/bin/sail npm run dev
適当にフォームページを作る
POST 先は dd()
で止めておく
use Inertia\Inertia;
Route::get('/form', function () {
return Inertia::render('Form');
});
Route::post('/form', function () {
dd(request()->input());
});
<template>
<form @submit.prevent>
<input v-model="form.name">
<button @click="onSubmit">保存</button>
</form>
</template>
<script setup lang="ts">
import { useForm } from '@inertiajs/vue3'
const form = useForm<{
name?: string
}>({
name: ''
})
const onSubmit = () => {
form.post('/form')
}
</script>
ででん!
useForm の挙動
通常使用
const form = useForm<{
name?: string
}>({
name: ''
})
引数を空にしてみる
const form = useForm<{
name?: string
}>({})
おやぁ...キーすら送られない
初期値を null にしてみる
const form = useForm<{
name?: string | null
}>({
name: null
})
これは問題なく送信できている
初期値を undefined にしてみる
const form = useForm<{
name?: string
}>({
name: undefined
})
ふぅむ、こちらも送られない
ただこの状態で input に文字を記入、削除を行うと、null となる
つまり、inputが空の状態だと null の扱いとなる
useForm の挙動の謎
弊社では useForm をダイアログを使ってよく使いまわしているのだが
useForm を使いまわそうと思ったら、初期値に無理やりキーと値を指定しないといけない
初期値で仮の値を入れて、使う際に再度値をセットする...
めんどくさい...
なんで型情報を読み取ってくれないんだろうか
ソースコードを読みに行ってみた
実際に post
など、値の送信を行う際は submit()
が実行される
その時に使われる値は data()
メソッドで取り出した値だ
submit(method, url, options: VisitOptions = {}) {
const data = transform(this.data())
で、この data()
は何をやっているかというと
data() {
return (Object.keys(defaults) as Array<keyof TForm>).reduce((carry, key) => {
carry[key] = this[key]
return carry
}, {} as Partial<TForm>) as TForm
},
わーお
この useForm
、既存のメソッドなどとフォームの値、分けないで保存してるのね
そのせいか defaults
というオブジェクトのキーを基準としているみたい
defaults
というのはこの useForm が作成されたタイミングで data をクローンしたものみたい
let defaults = typeof data === 'object' ? cloneDeep(data) : cloneDeep(data())
この時の data
は、useForm(ここ)
を示す
つまり、途中でなんか弄っても変わることはないんですね
このオブジェクトのキーを基準としているので、初期値が {}
だと何も送信されない結果となる
TS は型情報が実行時に消されるため、このような実装になったのではないか
型情報を for 文で回したりできれば、生成関数が作れるが
いっそ、キーだけ指定できる関数があれば解決??
ちなみに undefined が送信されないのは、確かajaxか何かの仕様だったはず(ソース不明)
対策
値の定義回数はできる限り減らしたい!
defaults() を使う
先ほど defaults
は useForm を作ったときにしか使えないと分かった
この値を変更する関数が用意されている
defaults(fieldOrFields?: keyof TForm | Partial<TForm>, maybeValue?: FormDataConvertible)
その後 reset()
メソッドを使用すると、useForm()
に値をセットしなおすことができる
reset(...fields)
それぞれ key に対して値を変更することもできるし、まとめて全フィールドを変更することもできる
これを使って
const form = useForm<{
name?: string
}>({})
const onSubmit = () => {
form.post('/form')
}
onMounted(() => {
form.defaults({
name: 'なまえ',
}).reset()
})
この使い方が賢そう
onMounted()
のとこを、ダイアログが開いたときの watch
合わせればいい感じでは