冒頭
- 個人開発でLaravel6.x + Vue.js(TypeScript)のSPA作ったので一部抜粋
- CSSフレームワークはVuetify2を採用
- 環境構築については一旦スルーします
- ソースコード例はこちら
ディレクトリ構造
フロント側の構造のみ抜粋
resources/ts
├── App.vue
├── app.ts
├── components
│ └── pages
│ └── Search.vue
├── laravel-data-entity
│ └── PaginateObject.d.ts
├── plugins
│ └── http.ts
├── router.ts
├── types
│ └── index.d.ts
└── vue-data-entity
└── DataObject.d.ts
ソースコード
型定義ファイル
- データベースのテーブルに用意してるテーブルの構造を定義する
resources/ts/vue-data-entity/DataObject.d.ts
export interface FileDataObject {
id: number;
hoge: string;
}
- Laravelのページネーションオブジェクトを定義する
resources/ts/laravel-data-entity/PaginateObject.d.ts
import { DataObject } from "../vue-data-entity/FDataObject"
export interface PaginateObject {
current_page: number;
data: DataObject[];
first_page_url: string;
from: number;
last_page: number;
last_page_url: string;
next_page_url: string;
path: string;
per_page: number;
prev_page_url: string | null;
to: number;
total: number;
}
Vue Routerの設定
resources/ts/router.ts
import Vue from 'vue'
import Router from 'vue-router'
import Search from './components/pages/Search.vue'
Vue.use(Router)
const router = new Router({
mode: 'history',
routes: [
{
path: '/search',
name: 'Search',
component: Search,
},
]
});
export default router;
Axiosをラップしたプラグインを作る
resources/ts/plugins/http.ts
import _Vue from 'vue';
import axios from 'axios';
export default {
install(Vue: typeof _Vue): void {
const http = axios.create({
responseType: "json"
});
Vue.prototype.$http = http;
},
};
プラグインをインストール、ルーターを設定
resources/ts/app.ts
import Vue from "vue";
import router from './router'
import App from "./App.vue";
~~省略~~
import http from './plugins/http';
Vue.use(http);
~~省略~~
new Vue({
router: router,
render: h => h(App),
~~省略~~
}).$mount('#app')
Vue側のソースコード例
resources/ts/components/pages/Search.vue
<template>
<v-content>
<v-container fluid>
<v-simple-table dense>
<template v-slot:default>
<thead>
<tr>
<th class="text-left">id</th>
<th class="text-left">hoge</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in data" :key="index">
<td>{{ item.id }}</td>
<td>{{ item.hoge }}</td>
</tr>
</tbody>
</template>
</v-simple-table>
<v-pagination v-model="page" :length="pageLength"></v-pagination>
</v-container>
<v-overlay :value="overlay">
<v-progress-circular color="primary" indeterminate size="64"></v-progress-circular>
</v-overlay>
</v-content>
</template>
<script lang="ts">
import { DataObject } from "../../vue-data-entity/DataObject";
import { PaginateObject } from "../../laravel-data-entity/PaginateObject";
import { Vue, Component, Watch } from "vue-property-decorator";
import VueRouter from "vue-router";
import { AxiosError, AxiosResponse } from "axios";
// registerHooksを登録しないとbeforeRouteUpdateが定義できません
Component.registerHooks(["beforeRouteUpdate"]);
export default class Search extends Vue {
data: FileDataObject[] = [];
overlay: boolean = false;
pageLength: number = 1;
page: number = 1;
/**
* created
*/
public created() {
this.search();
}
/**
* watch
* ルーティングに反映するためにWatchを使います。
*/
@Watch("page")
onPageChanged() {
this.$router
.push({
name: "Search",
query: {
page: this.page.toString()
}
})
}
/**
* search
*/
public search() {
this.page = isNaN(Number(this.$route.query.page))
? 1
: Number(this.$route.query.page);
this.overlay = true;
Vue.prototype.$http
.get(
`/api/search?page=${this.page}`
)
.then((res: AxiosResponse<PaginateObject>): void => {
this.data = res.data.data;
this.pageLength = res.data.last_page;
this.overlay = false;
})
.catch((error: AxiosError): void => {
alert("検索実行時にエラーが発生しました");
this.overlay = false;
});
}
/**
* beforeRouteUpdate
* ルーティングが更新される毎に(this.pageが変更する毎に検索を実行します)
*/
public beforeRouteUpdate(to: VueRouter, from: VueRouter, next: any) {
next();
this.search();
}
}
</script>
resources/ts/App.vue
<template>
<v-app id="inspire">
<router-view />
</v-app>
</template>
Laravel側
APIのルーティングを定義
routes/api.php
Route::get('search', 'Api\SearchController@search');
APIのコントローラーでページネーションオブジェクトを返却する
app/Http/Controllers/Api/SearchController.php
<?php
namespace App\Http\Controllers\Api;
use App\Data; //モデルを定義しといてね
use App\Http\Controllers\Controller;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
class SearchController extends Controller
{
/**
*
*
* @return
*/
public function search(Request $request)
{
return Data::select('id', 'hoge')->paginate(10);
}
}
resources/views/index.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# website: http://ogp.me/ns/website#">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="robots" content="index,follow">
<title>~~~~</title>
</head>
<body>
<div id="app" >
</div>
<script src="{{ mix('/js/app.js') }}"></script>
</body>
</html>
##動作例