Laravel 8でInertia+Vuetifyでテンプレート対応 - Laravel JetStream - お試し
はじめに
経緯
Laravel 8でInertia+Vuetifyを使ってみるの続きです。
前回作成したDataTables.vue
は、<v-app>
を直接記述していました。
これにLaravel JetStreamのInertiaのレイアウト構成を適用してみます。
Inertiaのレイアウト構成について
InertiaでHTTPレスポンスを生成してくれるInertia::render($component, $props)
の$component
は、resources/js/Pages
以下のVueコンポーネントを指定します。ここにメインコンテンツのコンポーネントを配置することになります。
Pages
ディレクトリと同じ階層には、Layouts
ディレクトリがあり、ここに画面レイアウトを構成するAppLayout.vue
というVueコンポーネントが配置されていました。つまりここにテンプレートとなるVueコンポーネントを配置することになります。
テンプレートの適用には、Vueのスロットが利用されています。(スロットについてはこちら)
Inertiaのレイアウト構成でVuetifyを組む
既存のコンポーネントを適当に利用し、レイアウト構成を組んでみます。
レイアウトコンポーネント準備
テンプレートと適当なメニューを作成します。
-
メニュー(Drawer)
Vuetifyの<v-navigation-drawer>
を利用したメニューを作ります。Commons
ディレクトリを作成し、Drawer.vue
というファイルを配置します。
内容はこんな感じにしました。resources/js/Commons/Drawer.vue<template> <v-navigation-drawer id="commons-drawer" v-model="drawer" expand-on-hover permanent app> <v-layout column> <v-list-item class="px-3"> <v-list-item-avatar size="32" color="grey"></v-list-item-avatar> <v-list-item-title class="title">メニュー</v-list-item-title> </v-list-item> <v-divider/> <template v-for="(link, index) in links"> <inertia-link :key="index" :href="route(link.name)" as="v-list-item"> <v-list-item-action> <v-icon>{{ link.icon }}</v-icon> </v-list-item-action> <v-list-item-content> <v-list-item-title v-text="link.title" /> </v-list-item-content> </inertia-link> </template> </v-layout> </v-navigation-drawer> </template> <script> export default { data () { return { drawer: true, links: [{ name: 'welcome', icon: 'mdi-home', title: 'Welcome' }, { name: 'dashboard', icon: 'mdi-view-dashboard', title: 'Dashboard' }, { name: 'data-tables', icon: 'mdi-view-list', title: 'DataTables' }], } } } </script> <style scoped> </style>
-
レイアウトテンプレート
次にレイアウトです。
Layouts
ディレクトリに、VueLayout.vue
という名称で作成します。ここで全体を
<v-app>
で括ります。
そこに先ほどのDrawer.vue
を配置します。ついでに<v-app-bar>
も配置します(空っぽです)そしてメインコンテンツです。
<v-main>
に<slot></slot>
を配置します。resources/js/Layouts/VueLayout.vue<template> <v-app> <commons-drawer/> <v-app-bar app/> <v-main> <v-container fluid> <slot></slot> </v-container> </v-main> </v-app> </template> <script> import CommonsDrawer from '@/Commons/Drawer' export default { name: 'VueLayout', components: {CommonsDrawer}, } </script> <style scoped> </style>
-
ルートに名前を設定
Welcome画面のルート設定にname('welcome')
を設定します。routes/web.phpRoute::get('/', function () { return Inertia::render('Welcome', [ : ]); })->name('welcome');
データテーブル画面の準備・表示
前回作成したDataTables.vue
を表示してみます。
-
テンプレート適用
DataTables.vue
を修正し、テンプレートを適用します。<script>
タグ内の適当な位置に以下を追加します。resources/js/Pages/DataTables.vue<script> import VueLayout from '@/Layouts/VueLayout' : components: { VueLayout, }, :
<template>
内の<v-app>
と<v-main>
の括りを<vue-layout>
に変更します。resources/js/Pages/DataTables.vue<template> <vue-layout> <v-card flat tile> : </v-card> </vue-layout> </template>
参考:DataTables.vue全体
resources/js/Pages/DataTables.vue<template> <vue-layout> <v-card flat tile> <v-app-bar flat outlined> <v-toolbar-title>サンプルテーブル</v-toolbar-title> </v-app-bar> <v-data-table :headers="headers" :items="desserts" :items-per-page="5" class="elevation-1" ></v-data-table> </v-card> </vue-layout> </template> <script> import VueLayout from '@/Layouts/VueLayout' export default { name: 'DataTables', components: { VueLayout, }, props: ['desserts'], data () { return { headers: [ { text: 'Dessert (100g serving)', align: 'start', sortable: false, value: 'name', }, { text: 'Calories', value: 'calories' }, { text: 'Fat (g)', value: 'fat' }, { text: 'Carbs (g)', value: 'carbs' }, { text: 'Protein (g)', value: 'protein' }, { text: 'Iron (%)', value: 'iron' }, ], } }, } </script> <style scoped> </style>
-
確認
ブラウザで確認してみます。
http://localhost/app02/data-tables
レイアウトが適用されていると思います。
こんな感じで、スロットを利用することで簡単にレイアウトを構成できました。
その他の画面にレイアウトを適用
せっかくなので、メニューからリンクされている他の画面にもレイアウトを適用します。
Dashboard.vue
コンポーネントを直接編集するところですが、別のやり方として編集せずにラップするようにします。
-
Dashboard画面
まずはダッシュボード画面です。
Pages
ディレクトリにVueDashboard.vue
というコンポーネントを作成します。
内容は以下のとおりです。resources/js/Pages/VueDashboard.vue<template> <vue-layout> <dashboard/> </vue-layout> </template> <script> import VueLayout from '@/Layouts/VueLayout' import Dashboard from '@/Pages/Dashboard' export default { name: 'VueDashboard', components: { VueLayout, Dashboard, } } </script> <style scoped> </style>
ルート設定の対象コンポーネントを変更します。
routes/web.phpreturn Inertia::render('Dashboard'); ↓ return Inertia::render('VueDashboard');
-
Welcome画面
続いてウェルカム画面です。
本来はこのテンプレートを適用するような画面ではないですが便宜上適用します。こちらも
Pages
ディレクトリの下にVueWelcome.vue
を以下の内容で作成します。
こちらはデータの受け渡しがありますので、v-bindの指定が必要です。resources/js/Pages/VueWelcome.vue<template> <vue-layout> <welcome v-bind="$props"/> </vue-layout> </template> <script> import VueLayout from '@/Layouts/VueLayout' import Welcome from '@/Pages/Welcome' export default { name: 'VueWelcome', components: { VueLayout, Welcome, }, props: { canLogin: Boolean, canRegister: Boolean, laravelVersion: String, phpVersion: String, } } </script> <style scoped> </style>
ルート設定のWelcome画面の対象コンポーネントを変更し、
routes/web.phpreturn Inertia::render('Welcome'); ↓ return Inertia::render('VueWelcome');
URIも
/
から/welcome
に調整します。routes/web.phpRoute::get('/', function () { ↓ Route::get('/welcome', function () {
/
へのアクセスは/dashboard
リダイレクトします。routes/web.phpRoute::get('/', function () { return redirect('/dashboard'); });
参考:web.php全体
routes/web.php<?php use Illuminate\Foundation\Application; use Illuminate\Support\Facades\Route; use Inertia\Inertia; Route::get('/', function () { return redirect('/dashboard'); }); Route::get('/welcome', function () { return Inertia::render('VueWelcome', [ 'canLogin' => Route::has('login'), 'canRegister' => Route::has('register'), 'laravelVersion' => Application::VERSION, 'phpVersion' => PHP_VERSION, ]); })->name('welcome'); Route::middleware(['auth:sanctum', 'verified'])->get('/dashboard', function () { return Inertia::render('VueDashboard'); })->name('dashboard'); Route::get('/data-tables', [\App\Http\Controllers\AnyController::class, 'responseDataTables'])->name('data-tables');
これで他の画面にもレイアウトが適用できたかと思います。
※Dashboardを表示するにはログインが必要です。
まとめ
Inertia + Vue + Vuetify でレイアウト構成を組んでみました。
VuetifyでSPAを作成するのと同じ感覚で構築できる、かと思いきや、Vuex(状態管理を行うライブラリ)を利用したSPAに慣れすぎていると、UIまわりの状態管理をどのようにすれば良いのか悩みます。(Vuexについてこちら)
例えば、今回のレイアウトのメニュー(Drawer)のオン・オフなどです。
Vuexを使えば簡単に管理できますが、このケースでVuexを使うのは行き過ぎに思えます。もっと軽量で良いのです。
少し調べたら、InertiaにShared dataという仕組みがあり、ページ間でデータを共有できるようです。実際にWelcome画面でも$page.props.user
というようにShared dataへのアクセスが行われています。
ただ、この仕組みは、値の参照こそ簡単ですが、値をセットするのが面倒に思えます。
となると、普通にCookieで良いのではないか、と思う次第です。
というわけで、使ったことのないvue-cookiesのことでも調べてみようか、と思い始めた検証でした。
こんなところで。
Discussion