0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Laravel Inertia】useForm を使った値初期化の最適解

Posted at

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() で止めておく

routes/web.php
use Inertia\Inertia;

Route::get('/form', function () {
    return Inertia::render('Form');
});

Route::post('/form', function () {
    dd(request()->input());
});
resources/js/Pages/Form.vue
<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>

ででん!

image.png

useForm の挙動

通常使用

const form = useForm<{
  name?: string
}>({
  name: ''
})

image.png

引数を空にしてみる

const form = useForm<{
  name?: string
}>({})

image.png

おやぁ...キーすら送られない

初期値を null にしてみる

const form = useForm<{
  name?: string | null
}>({
  name: null
})

image.png

これは問題なく送信できている

初期値を undefined にしてみる

const form = useForm<{
  name?: string
}>({
  name: undefined
})

image.png

ふぅむ、こちらも送られない

ただこの状態で input に文字を記入、削除を行うと、null となる

image.png

つまり、inputが空の状態だと null の扱いとなる

useForm の挙動の謎

弊社では useForm をダイアログを使ってよく使いまわしているのだが
useForm を使いまわそうと思ったら、初期値に無理やりキーと値を指定しないといけない

初期値で仮の値を入れて、使う際に再度値をセットする...
めんどくさい...

なんで型情報を読み取ってくれないんだろうか :thinking:
ソースコードを読みに行ってみた

実際に 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()
})

image.png

この使い方が賢そう

onMounted() のとこを、ダイアログが開いたときの watch 合わせればいい感じでは

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?