Inertia.jsでXHRリクエスト

## はじめに
今回も、前回の続編として引き続きInertia.jsについて書いていきたいと思います。

https://inertiajs.com/

前回の記事では、CRUD処理でいうと一覧（Read）の内容のみだったので、
今回は、追加・編集・削除などにも対応できるようにXHRリクエストの内容を書いていけたらと思います。

前回の記事はこちら。https://qiita.com/okok-97/items/d7f660bea951e6be0758

## XHRリクエストとは
XHRリクエストとは、XMLHttpRequestの略で、JavascriptからサーバーへHTTPリクエストを行うことです。

SPAでは、初回のアクセス時にすべてのHTML構造を読み込み、2回目以降は必要なデータだけを必要に応じて読み込んで画面を更新していきます。その必要なデータを読み込む際に必要なのが、XHRリクエストです。

初回のアクセスか2回目以降のアクセスかどうかの判定は、「X-Inertia」ヘッダーがTrueにセットされているかどうかで判別しています。
「X-Inertia」ヘッダーがセットされていなければ(初回のアクセス)、通常のリクエストとみなして、ページを構成するHTMLをレスポンスとして出力、
「X-Inertia」ヘッダーがTrueにセットされている場合(2回目以降)、Inertia.jsによるXHRリクエストと判定し、ページオブジェクトのJSONデータのみをレスポンスとして返します。



## Inertia.jsによるXHRリクエスト
まずInertia.jsでXHRリクエストをする場合の処理全体の流れ(ユーザーを新規追加した場合)を記述します。


```c#:src/resources/js/Pages/UserEdit.vue
<template>
    <form v-on:submit.prevent="userEdit">
        <p>名前</p>
        <input type="text" v-model="form.name">
        <button>追加</button>
    </form>
</template>

<script>
    export default {
        data() {
            return {
                form: {
                    name: '',
                },
            }
        },
        methods: {
            userEdit() {
                this.$inertia.visit(this.route('user.create', this.form), { method: 'post' })
            },
        }
    }
</script>
```







```c#:src/routes/web.php
Route::get('/user', function () {
    return Inertia::render('UserEdit');
})->name('user');
Route::post('/user/create', 'UserController@create')->name('user.create');
```





```c#:src/app/Http/Controllers/UserController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\User;
use Inertia\Inertia;
use Illuminate\Support\Facades\DB;

class UserController extends Controller
{
    public function create(Request $request) {
        $user = new User;
        $user->name = $request->name;
        $user->save();
        DB::commit();
        return redirect()->route('user');
    }
}
```

上記のように、Inertia.jsでは、$inertia.visitを使うことでXHRリクエストが可能になります。

UserEdit.vueの`this.$inertia.visit`の部分では、名前付きルートが`'user.create'`のルートに対して、formのデータをPOSTリクエストして、コントローラーでユーザー情報を追加しています。
追加後は、名前付きルートが`'user'`のルートに対してリダイレクトされています。


オプションは、mehodオプションの他に下記のオプションが存在します。


```c#:src/resources/js/Pages/UserEdit.vue
methods: {
    userEdit() {
        this.$inertia.visit(route('user.create'),{
            method: 'post', //POSTメソッドで送信
            data:{
                form: this.form, //送信データを指定
            },
            //履歴管理
            replace: true,
            //状態の保持
            preserveState: true,
            //スクロールポジションの保持
            preserveScroll: true,
            //リクエスト送信前に確認ダイアログを表示
            onBefore: (visit) => {
                console.log('onBefore', visit);
                confirm('保存しますか?')
            },
            //リクエスト開始時
            onStart: (visit) => {
                console.log('onStart', visit);
            },
            //リクエスト進行中
            onProgress: (progress) => {
                console.log('onProgress', progress);
            },
            //リクエスト成功時
            onSuccess: (page) => {
                console.log('onSuccess', page);
            },
            //エラー発生時
            onError: (errors) => {
                console.log('onError', errors);
            },
            //リクエストキャンセル時
            onCancel: () => {
                console.log('onCancel');
            },
            //リクエスト完了時
            onFinish: visit => {
                console.log('onFinish', visit)
            },
        });
    }
}
```


## これでもいける(Inertia.post)
```c#:src/resources/js/Pages/UserEdit.vue
<template>
    <form v-on:submit.prevent="userEdit">
        <p>名前</p>
        <input type="text" v-model="form.name">
        <button>登録</button>
    </form>
</template>

<script>
    import { Inertia } from '@inertiajs/inertia'

    export default {
        data() {
            return {
                form: {
                    name: '',
                },
            }
        },
        methods: {
            userEdit() {
                Inertia.post(route('user.create'), this.form)
            },
        }
    }
</script>
```
## これでもいける(Inertia link)
```c#:src/resources/js/Pages/UserEdit.vue
<template>
    <div>
        <p>名前</p>
        <input type="text" v-model="name">
        <Link as="button" type="button" method="post" :href="route('user.create')" :data="{name: name}">追加</Link>
    </div>
</template>

<script>
    import { Link } from '@inertiajs/inertia-vue3'

    export default {
        components: {
            Link,
        },
        data() {
            return {
                name: '',
            }
        }
    }
</script>
```
Inertia.jsには、リンクをXHRリクエストに変換してくれるInertia linkというものがあります。


```c#:aタグの場合
<a href="/">Home</a>
```
```c#:Inertialinkの場合
<Link href="/">Home</Link>
```

aタグとInertia linkの違いとしては、
ページ移動の度にすべてのデータを読み直すHTTPリクエストが発生するaタグに対して、
Inertia linkの場合、必要なデータだけをリクエストするXHRリクエストを行います。

SPAでは、必要なデータだけを必要に応じて読み込んで画面を更新するのが正しい挙動なので、
Inertia linkの方が、ブラウザがリロードされることなく、SPAらしい動きをします。


Inertia linkにも同じく下記の通りオプションがあります。

```c#:src/resources/js/Pages/UserEdit.vue
// 履歴管理
<Link href="/" replace>Home</Link>

// 状態の保持
<input v-model="name" type="text" />
<Link href="/search" :data="{ name }" preserve-state>Search</Link>

// スクロールポジションの保持
<Link href="/" preserve-scroll>Home</Link>

// 特定のデータだけを読み込む
<Link href="/" :only="['uses']">Home</Link>
```



## おまけ

Inertiaで別のページに遷移させたい場合、`return redirect()->route('user');`のrouteの部分を遷移させたいページの名前付きルートを指定してあげることでできますが、

外部のサイト、またはInertiaではないページへリダイレクトさせたいとき、コントローラー側でInertia::location()メソッドを利用します。

これにより409Conflictレスポンスが作成され、X-Inertia-Locationヘッダーにリダイレクト先のURLが含まれます。それをInertiaがフロント側で検知し、windows.location = url(X-Inertia-Locationヘッダーに含まれたurl)を実行してページを移動させます。

```c#:src/app/Http/Controllers/UserController.php
return Inertia::location($url);
```