Inertia linkの使い方 Inertia.js入門 #5

Laravel

この記事では、Inertia linkについて解説します。

この連載記事ではLaravelにおけるInertia.jsの使い方を解説しています。他の記事と合わせてお読みください。

Laraveler
Laraveler

Inertia linkとは一体何でしょうか?aタグのリンクではどうしてダメなんですか?

シップ
シップ

aタグだと通常のリクエストとなり、Inertia.jsの特徴であるSPAにならないため、リンクをXHRリクエストに変換してくれるInertia linkを使用します。

Inertia linkとは

通常のaタグによるリンクでは、ページ移動の度にすべてのデータを読み直すHTTPリクエストが発生します。Inertiaアプリの本髄は、初回にフルHTMLなデータを読み込んだ後は、次からのページアクセス時には必要なデーだけを読み込むSPAです。

Inertia linkは、aタグをクリックした際に、通常のHTTPリクエストを行う代わりに必要なデータだけをリクエストするXHRリクエストを行うための仕組みです。

Inertia.jsを使用する際には必ず使うことになるので使い方をしっかり覚えておきましょう。

Inertia linkの作成

aタグ

<a href="/">Home</a>

このような通常のaタグによるリンクをInertia linkに変更するには、aタグをinertia-linkタグに書き換えるだけです。

<inertia-link href="/">Home</inertia-link>

これで、クリック時にXHRリクエストに変更するイベントハンドラの設定されたaタグが出力されます。装飾などは通常のaタグとして行えます。

buttonリンク

aタグではなく、buttonタグでリンクを作成したい場合はas属性でbuttonを指定します。

<inertia-link href="/logout" method="post" as="button" type="button">Logout</inertia-link>

// これは次のようなタグが出力されます:
<button type="button">Logout</button>

Inertia linkの属性

HTTPメソッド

Inertia linkはデフォルトではGETリクエストを行いますが、method属性で他のメソッドを使用するよう指定することもできます。対応メソッドはGETの他、POST, PUT, PATCH, DELETEです。

//POSTメソッドでリクエストを行う場合
<inertia-link href="/logout" method="post">Logout</inertia-link>

Data属性

data属性で送信するリクエストに付加するデータを指定できます。data属性にはオブジェクトか、FormDataインスタンスを指定できます。

<inertia-link href="/endpoint" method="post" :data="{ foo: bar }">Save</inertia-link>

カスタムヘッダー

headers属性で、リクエストに追加するHTTPヘッダーを指定できます。Inertiaがデフォルトで付加するヘッダーは上書きできません。

<inertia-link href="/endpoint" :headers="{ foo: bar }">Save</inertia-link>

履歴管理

デフォルトでは、Inertiaリンクによるページ移動では、ブラウザの履歴に移動したページが追加されていき、戻ったり進んだりできるようになっています。

replace属性をつけることで、ページ移動の際に履歴を追加するのではなく、現在の履歴を書き換えるようにすることができます。

<inertia-link href="/" replace>Home</inertia-link>

同じURLへのリクエストの場合は履歴は追加されず、replace属性が付いた状態となります。

状態の保存

preserve-state属性をつけると、ページ移動時にフォームに入力した状態を維持させることができます。

<input v-model="query" type="text" />

<inertia-link href="/search" :data="{ query }" preserve-state>Search</inertia-link>

Q:レイアウト機能でもフォームの状態を保存できますよね?

A:レイアウト機能では、読み直す部分ではないページの状態が維持されました。この読み込むページ自身にあるフォームの状態が維持されます。

スクールの抑制

preserve-scroll属性をつけると、ページ移動時にスクロールポジションがリセットされなくなり、移動前と同じスクロール位置が維持されるようになります。

<inertia-link href="/" preserve-scroll>Home</inertia-link>

部分的なデータ読み込み

そのページに含まれるデータのうち、ある特定のデータだけを読み込めばいい場合、only属性でその読み込むデータを指定できます。

<inertia-link href="/users?active=true" :only="['users']">Show active</inertia-link>

サンプルをモデル使用版へ

このサンプルは前回から引き継いで使用しています。ソースコードはGitHubからもご覧いただけます。

モデル作成

前回まで作成していたブックマーク一覧を改良します。モデルを作成して実際にブックマークを追加できるようにします。

php artisan make:model Bookmark -ms
//略・・・
    public function up()
    {
        Schema::create('bookmarks', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('url');
            $table->timestamps();
        });
    }
//略・・・
php artisan migrate

シーダーの追加

<?php

namespace Database\Seeders;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Seeder;

class BookmarkSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('bookmarks')->insert([
            [
                'title' => 'SOHO MIND',
                'url' => 'https://blog.shipweb.jp/',
            ],
            [
                'title' => 'Youtube Checker',
                'url' => 'https://ytc.shipweb.jp/',
            ],
        ]);
    }
}
php artisan db:seed --class=BookmarkSeeder

コントローラーの変更

コントローラーもモデルを使用する仕様に変更します。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Inertia\Inertia;
use App\Models\Bookmark;

class BookmarkController extends Controller
{
    //    
    public function index () 
    {
        return Inertia::render('Bookmark/Index',['bookmarks' => Bookmark::all()]);
    }
    
    //検索メソッド
    public function search ($queryWord) 
    {
        return Inertia::render('Bookmark/Index',[
            'bookmarks' => Bookmark::Where(
                                'title', 'like', '%'.$queryWord.'%'
                            )->orWhere(
                                'url', 'like', '%'.$queryWord.'%'
                            )->get()
        ]);
    }

    //追加メソッド
    public function store (Request $request) 
    {
        $bookmark = new Bookmark;
        $bookmark->title = $request->title;
        $bookmark->url = $request->url;
        $bookmark->save();

        return redirect()->route('bookmark.index', $parameters = [], $status = 303, $headers = []);
    }
}

ルートの追加

この2つのルートを新たに追加します。検索と追加用です。

//下の2行を追加
Route::get('/Bookmark/search/{queryWord}',[App\Http\Controllers\BookmarkController::class, 'search'])->where('queryWord', '.*')->name('bookmark.search');
Route::post('/Bookmark/store',[App\Http\Controllers\BookmarkController::class, 'store'])->name('bookmark.store');

Inertia-linkの実例

検索ボタンの変更

検索ボタンです。

  • as属性にbuttonを指定してボタンにします。
  • method属性にgetを指定してGETリクエストを行います。
  • :href属性でリンク先を指定します。名前付きルートを使用して引数に検索ワードを渡しています。
  • preserve-state属性で、フォームの内容がページ読み込み時に残るようにしています。
<inertia-link as="button" method="get" :href="route('bookmark.search',{queryWord: searchWord})" preserve-state class="border border-gray-400 m-1 p-1 text-sm">ブックマーク検索</inertia-link>

ブックマーク追加ボタン

ブックマーク追加するために、タイトルとURLを入力するボックスと追加ボタンを追加しました。
追加ボタンはこのような感じです。

  • method属性で、POSTリクエストにしています。
  • data属性で、送信するデータを指定しています。titleに新規タイトル、urlに新規URLを渡しています。
<inertia-link as="button" method="post" :href="route('bookmark.store')" :data="{title: newTitle, url: newUrl}" class="border border-gray-400 m-1 p-1 text-sm">ブックマーク追加</inertia-link>

ブックマーク削除ボタン

各ブックマークのリストの右側にブックマーク削除用のボタンを追加しました。

  • method属性で、DELETEリクエストにしています。
  • only属性をつけて、応答データとして「bookmarks」だけを読み込むように指定しています。
<inertia-link as="button" method="delete" :href="route('bookmark.delete',bookmark.id)" preserve-scroll :only="['bookmarks']" class="border border-red-400 m-1 p-1 text-sm text-red-400">削除</inertia-link>

only属性をつけた時の実際の挙動

only属性をつけると部分的なデータを読み込むというのがいまいちピンとこない方のために、only属性をつけた場合と付けない場合とでどう変わるのかをデベロッパーツールで見てみます。

only属性をつけない通常のリクエスト。bookmarks以外にもjetstreamやuserなどのデータが読み込まれています。
only属性をつけるとpropsで渡されるデータは先ほどあったjetstreamなどは渡されずbookmarksだけになります。

このように、only属性をつけると通信量の節約になります。ただこれだけではLaravel側でデータは取得しているので、サーバー側の負荷は変わっていません。データ自体を取得しないようにするにはコントローラー側でもう一工夫必要になります。

//これだと毎回取得してしまう
return Inertia::render('Bookmark/Index',['bookmarks' => Bookmark::all()]);

//これで必要な時だけ取得するようになる
return Inertia::render('Bookmark/Index',['bookmarks' => fn() => Bookmark::all()]);

サンプルソース

削除や追加ボタンを加えた「Index.vue」はこのようになります。

<template>
    <layout>
        <h2 class="text-lg border-b p-1">
            <inertia-link :href="route('bookmark.index')" class="text-blue-700 underline">
            ブックマーク一覧
            </inertia-link>
        </h2>
        <ul class="list-disc list-inside p-2">
            <li v-for="bookmark in bookmarks" class="list-none flex justify-between">
                <a :href="bookmark.url" target=_blank class="text-blue-700" >{{ bookmark.title }}</a>
                <inertia-link as="button" method="delete" :href="route('bookmark.delete',bookmark.id)" preserve-scroll :only="['bookmarks']" class="border border-red-400 m-1 p-1 text-sm text-red-400">削除</inertia-link>
            </li>
        </ul>
        <h3 class="border-l-4 p-1">ブックマーク追加</h3>
        <div class="flex w-60">
            <div class="w-20 text-right bg-gray-200 text-sm p-1 m-1">タイトル</div>
            <div class="w-40">
                <input type=text name=newTitle v-model="newTitle" size=15 class="p-1 m-1 text-sm w-full" />
            </div>
        </div>
        <div class="flex w-60">
            <div class="w-20 text-right bg-gray-200 text-sm p-1 m-1">URL</div>
            <div class="w-40">
                <input type=text name=newUrl v-model="newUrl" size=15 class="p-1 m-1 text-sm w-full" />
            </div>
        </div>
        <inertia-link as="button" method="post" :href="route('bookmark.store')" :data="{title: newTitle, url: newUrl}" class="border border-gray-400 m-1 p-1 text-sm">ブックマーク追加</inertia-link>
    
    </layout>
</template>

<script>
    import Layout from '@/Pages/Layout'

    export default {
        components: {
            Layout,
        },
        data:function(){
            return {
                newTitle: '',
                newUrl: '',
            }
        },
        props:{
            bookmarks: {
                type: Array,
            }
        },
        mounted: function(){
            document.title = "ブックマーク一覧";
        }
    }
</script>

「Layout.vue」はこのようになります。

<template>
    <html>
        <header class="bg-gray-100">
            <inertia-link href="/" class="text-blue-700 underline m-2">Home</inertia-link>
            <inertia-link href="/HelloWorld" class="text-blue-700 underline m-2">HelloWorld</inertia-link>
            <inertia-link :href="route('bookmark.index')" class="text-blue-700 underline m-2">ブックマーク一覧</inertia-link>
            <input type=text name=search v-model="searchWord" size=15 class="p-1 m-1 text-sm" />
            <inertia-link as="button" method="get" :href="route('bookmark.search',{queryWord: searchWord})" preserve-state class="border border-gray-400 m-1 p-1 text-sm">ブックマーク検索</inertia-link>
        </header>
        <article class="max-w-screen-sm">
            <slot />
        </article>
    </html>
</template>

<script>
  export default {
      data:function(){
          return {
              searchWord: ''
          }
      }
  }
</script>

完全なソース一覧はGitHubからご覧ください。

表示例
今回のまとめ
  • Inertia-linkはaタグを拡張する
  • 各種の属性をつけることで、追加のデータを渡したり、動作を変更できる。

次回はリンク以外からでもInertiaリクエストを発行するためのInertia.visit()を取り上げます。

Inertia.js入門記事一覧
Inertia.jsでシンプルにSPAを構築する Inertia入門#1
Inertia.jsを使うとLaravelやRubyonRailsなどのフレームワーク上でAjax用のAPIやコントローラーを作成しなくても、通常のビューを使う要領でSPA(シングルページアプリケーション)が構築できます。
Laravel+Inertia.jsのインストールとHello World Inertia入門#2
この記事ではLaravelにInertia.jsをインストールしてHelloWorldを出力する方法までを解説します。
Inertia.jsのルーティングとレスポンス作成 Inertia入門#3
Laravel8でjetstreamを入れるとLimewireかInertiaかを選べます。この記事ではInertiaを使ってルーティングやInertiaレスポンスの返し方を解説します。
Inertia.jsでページレイアウト Inertia入門#4
この記事では、ページレイアウトについて解説します。親レイアウトに子ページを組み込むことで、ヘッダーやフッターを共通化できます。
Inertia linkの使い方 Inertia.js入門 #5
この記事では、Inertia linkについて解説します。
InertiaでXHRリクエスト Inertia.js入門 #6
この記事では、Inertia.jsで非同期通信を行うInertia.visit()について解説します。
Inertia.jsでのフォームとリダイレクト Inertia.js入門 #7
この記事では、Inertia.jsでのフォーム送信とリダイレクトについて解説します。
Inertiaでのバリデーション Inertia.js入門 #8
この記事では、Inertia.jsを使った際のバリデーションエラーの表示について解説します。
Inertiaで共通データとフラッシュメッセージを毎回のレスポンスに含める Inertia.js入門 #9
この記事では、Inertiaのレスポンスに毎回同じデータを含める方法やフラッシュメッセージの使い方について解説します。

Comments

タイトルとURLをコピーしました