
九保すこひです(フリーランスのITコンサルタント、エンジニア)
さてさて、Laravel
はバージョンが8.x
になった現在でも野心的に新機能を追加ていますが、そういった中でも初めて見た時に「これはすごい!」と思ったのが・・・・・・
ページリンク(パジネーション)
でした。
この機能は、例えば以下のようにpaginate()
を使ってDBからデータを取ってくるとします。
$users = \App\Models\User::paginate();
すると、なんと以下のようにするだけで、ページ送りリンク(パジネーション)を表示してくれるんですね。
{{ $users->links() }}
表示例はこちら。
※ こちらはBootstrap
を使った場合です。Laravel 8.x
でBootstrap
を使ったページリンクを表示する方法は おまけ をご参照ください。
実はこのページリンク、見た目はシンプルなんですが実は自分でつくるのは結構めんどうだったりするので、とても重宝していました。
しかし、最近私の環境ではVue + Ajax
でデータを取得することが多くなったので、Laravel
のページリンクを使うことは減ってきました。
そして、今回のテーマが立ちはだかりました。
VueでもLaravelと同じようなページリンクを作りたい(けっこう切実)
と。
そこで
今回はLaravel
で取得したデータを使ってVue
でも同じようにページリンクを作る方法をご紹介します。ぜひ学習の参考になりましたら嬉しいです
(最後に今回実際に開発したソースコード一式をダウンロードできますよ)
「動画の編集ってめちゃくちゃ
大変なんですね・・・」
開発環境: Laravel 8.x、Vue 3、axios 0.19
目次 [非表示]
前提として
テーブルはどこでもいいのですが、以下のように50件程度のデータを用意しておいてください。
もしデータを用意していない場合は、以下の項目を参考にしてダミーのユーザーデータを作っておいてください。
参考記事: 新しいFactoryでテストユーザーをつくってログインしてみる
ルートをつくる
まずは以下2つのルートを追加します。
- Vue のページリンクを表示するルート
- Ajax でデータを取得するルート
実際にはこうなります。
routes/web.php
Route::get('vue_pagination', function(){ return view('vue_pagination'); });
Route::get('users', function(){ return \App\Models\User::paginate(5); });
ビューをつくる
続いて上のルートで設定したビュー(テンプレート)を作ります。
resources/views/vue_pagination.blade.php
<html>
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
</head>
<body>
<div id="app" class="p-3">
<!-- ⑥ 取得したデータの表示 -->
<div v-for="user in users.data" v-if="users">
<div v-text="user.name"></div>
</div>
<br>
<!-- ⑦ 独自コンポーネントを実行する -->
<v-pagination :data="users" :link-max="7" @page-move="onMovePage"></v-pagination>
</div>
<script src="https://unpkg.com/vue@3.0.2/dist/vue.global.prod.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script>
<script>
// ① ページリンク・コンポーネントを定義する
const paginationComponent = {
// ② プロパティを定義
props: {
data: {
type: Object,
default: {}
},
linkMax: {
type: Number,
default: 5
}
},
methods: {
moveTo(page) {
document.activeElement.blur(); // クリックされたときのフォーカスを外す
this.$emit('page-move', parseInt(page)); // ③ 独自のイベントを送出
},
movePrev() {
const page = Math.max(this.currentPage - 1, 1); // ⑤-1 1より少ないときは1になる
this.moveTo(page);
},
moveNext() {
const page = Math.min(this.currentPage + 1, this.lastPage); // ⑤-2 最大ページより大きいときは最大ページになる
this.moveTo(page);
},
classes(page) {
return (parseInt(page) === this.currentPage) ? 'active' : '';
}
},
computed: {
hasData() {
return (Object.keys(this.data).length > 0);
},
currentPage() {
return parseInt(this.data.current_page);
},
lastPage() {
return parseInt(this.data.last_page);
},
pages() { // ④ 表示すべきページ番号を計算
let pages = [];
const lastPage = this.lastPage;
for(let i = 1 ; i <= lastPage ; i++) {
pages.push(i);
}
if(pages.length > this.linkMax) {
const pageIndex = this.currentPage - 1;
const leftMaxPage = Math.floor(this.linkMax * 0.5);
const rightMaxPage = this.linkMax - leftMaxPage;
const leftDiff = pageIndex - leftMaxPage;
const rightDiff = pageIndex + rightMaxPage - pages.length;
let start = (leftDiff >= 0)
? leftDiff - Math.max(0, rightDiff)
: 0;
const end = start + this.linkMax;
pages = pages.slice(start, end);
}
return pages;
}
},
template: `
<div v-if="hasData">
<ul class="pagination">
<li class="page-item">
<a class="page-link" href="#" aria-label="Previous" @click.prevent="movePrev()">
<span aria-hidden="true">«</span>
</a>
</li>
<li class="page-item" v-for="p in pages" :class="classes(p)">
<a class="page-link" href="#" v-text="p" @click.prevent="moveTo(p)"></a>
</li>
<li class="page-item">
<a class="page-link" href="#" aria-label="Next" @click.prevent="moveNext()">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</div>
`
};
Vue.createApp({
data() {
return {
users: {}
}
},
methods: {
onMovePage(page = 1) { // ⑧ Ajaxでデータを取得する
const url = '/users?page='+ page;
axios.get(url)
.then(response => {
this.users = response.data;
});
}
},
mounted() {
// ⑨ ページ表示後すぐデータを取得
this.onMovePage();
}
})
.component('v-pagination', paginationComponent) // Vueにコンポーネントをセットする
.mount('#app');
</script>
</body>
</html>
今回は、ここがメインですのでひとつずつご紹介します。
① ページリンク・コンポーネントを定義する
まず、Vue
コンポーネントを定義します。
目的は、以下のようにするだけでLaravel
と同じようなページリンクを表示できるようにするためです。(つまり、使い回しができるので次からラクできるというわけですね)
<v-pagination :data="data"></v-pagination>
定義したコンポーネントは、以下のようにセットすることで有効になります。
Vue.createApp({
// 省略
})
.component('v-pagination', paginationComponent) //
Vueにコンポーネントをセットする
.mount('#app');
② プロパティを定義
コンポーネントに以下2つのデータがセットできるようにプロパティを定義します。
- data: Laravel の pagenate() で取得したデータ
- linkMax: ページリンクを最大いくつ表示するか ※
※ 例えば、以下は5
のとき(初期値は5
にしてます)
そして、以下が7
のときです。
③ 独自のイベントを送出
独自のイベントpage-move
を送っているのですが、これはページリンクがクリックされたときに実行されれるもので、コンポーネントで指定した@page-move="******"
の部分が実行されることになります。
<v-pagination :data="users" @page-move="onMovePage"></v-pagination>
④ 表示すべきページ番号を計算
pages()
は、「表示すべきページリンクがどこからどこまでなのか?」を計算する部分になります。
⑤ 次へと前へのリンクがクリックされたときの挙動
この部分で、「ありえないページ番号」の場合は修正をしてからpage-move
イベントを実行するようにしています。
⑥ 取得したデータの表示
ここで、Ajax
で取得したデータの一覧表示をしています。
⑦ 独自コンポーネントを実行する
有効になったコンポーネントを実際に使っている部分です。
コンポーネントの定義でも紹介しましたが、data
の部分にはLaravel
のpaginate()
で取得したデータをセットすることになります。
<v-pagination :data="users" @page-move="onMovePage"></v-pagination>
また、@page-move="*****"
の部分は独自に設定したイベント「page-move」が送出されたときにonMovePage
が実行されるようにしています。
⑧ Ajaxでデータを取得する
axios
という「Ajax送信のためのJavaScript
パッケージ」を使ってデータ取得しています。取得するのは、ルートとして作った以下の部分です。
Route::get('users', function(){ return \App\Models\User::paginate(); });
なお、onMovePage
が呼ばれるのは、ページ表示後すぐと、ページリンクがクリックされた時(つまり、page-move
イベントが送出された時)の2ヵ所です。(そのため、共通化しています)
⑨ ページ表示後すぐデータを取得
mounted()
はページが表示されるとすぐに実行されるメソッドなので、この中でデータ取得するようにしています。
テストしてみる
では、実際にテストしてみましょう
まずは、「http://******/vue_pagination」にブラウザでアクセスします。
一覧とページリンクが表示されています。
では、「»(次へ)」リンクをクリックしてみましょう。
ページが移動になり、一覧データも変更になっています。
では、次に6ページ目のリンクをクリックしてみましょう。
はい
6ページ目が表示されて、さらに、1
と2
ページ目のリンクは表示されていません。(最大7件表示なので)
成功です
ダウンロードする
今回実際に開発したソースコード一式を以下からダウンロードできます。
LaravelのページリンクをVueで作る※ ただし、テストデータなどはご自身で用意していただく必要があります。
おまけ:Laravel 8.xのページリンクをBootstrapで表示する方法
Laravel 8.x
からはメインのCSSフレームワークがTailwindCss
に変更になっていますので、長年利用されていたBootstrap
ではページリンクがうまく表示されなくなっています。
そのため、もしBootstrap
をつかってページリンクを表示したい場合はAppServiceProvider
に以下の内容を追加してみてください。
app/Providers/AppServiceProvider.php
<?php
// 省略
use Illuminate\Pagination\Paginator; //
ここを追加しました
class AppServiceProvider extends ServiceProvider
{
// 省略
public function boot(Charts $charts)
{
Paginator::useBootstrap(); //
ここを追加しました
}
}
おわりに
ということで、今回はLaravel
のページリンクをVue
でも使えるようにしてみました。
コンポーネントで実装しているので、また別の場所で必要になっても簡単に<v-pagination></v-pagination>
タグを使うだけで実装できますよ
なお、今回はVue 3
を使っていますが、Vue 2
でもpaginationComponent
の中身はそのままでつかえると思います。
ぜひ皆さんも試してみてくださいね。
ではでは〜
「今年は、新しい事業とかも考えよっかな・・・」